如果各位对不熟悉JAVA的 HashMap原理和实现,那么这篇文章可能值得一看。

HashMap 简介: 基于哈希表的 Map 接口的非同步实现。允许使用null值和null键。键不允许重复,值允许重复。存储是无序的,是按照哈希散列排序的。底层数据结构:Hash链表。

图示:

hashmap java key 中文 hashmap java实现_JAVA自己实现HashMap

一 :实现原理(结合JDK源码片段):

1.初始化HashMap : 更具给定的参数初始化一个数据类型为Node<K,V>的table数组。

transient Node<K,V>[] table;

2.存入键值对<K,V>:

2.1 判断该table是否为空,若为空则初始化该table。

2.2 判断该key是否为null,若为null ,则将该<K,V>放置到数组的第一个位置。否则继续。

2.3 先使用hash(k) 计算得到该k对应的hash数值。

//计算key的 hash数值
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

2.4 使用该hash 对整个表的长度进行取模运算,以求该k能放在数组中的坐标I 。

tab[i = (n - 1) & hash]


2.5判断table[i]处是否有数据,若不为空,则遍历该位置的链表,比较是否有相同的key,如果有相同的key则用的新的V覆盖旧的V,否则在该链表的顶部插入该节点。

判断是否为同一个key:


if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))

3.获取key对应的value。步骤和存入数据差不多。


二.MyHashMap的代码实现(肯定有不足之处):

import java.io.Serializable;
import java.util.*;


//zhaoke 2018-1-8
public class MyHashMap<K, V> implements Cloneable, Serializable {

    //设置初始的容量为16
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

    //设置其最大容量为1024
    static final int MAXIMUM_CAPACITY = 1 << 30;

    //设置其容量阈值因子为0.75
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    //实际储存的大小
    transient int size;

    //阈值
    int threshold;


    //设置每一个节点 实现 Map.Entry<K,V>  是为了实现一个链表
    static class Node<K, V> implements Map.Entry<K, V> {
        final int hash;
        final K key;
        V value;
        MyHashMap.Node<K, V> next;

        Node(int hash, K key, V value, MyHashMap.Node<K, V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey() {
            return key;
        }

        public final V getValue() {
            return value;
        }

        public final String toString() {
            return key + "=" + value;
        }

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);  // 调用 Objects的hashCode() 进行异或计算
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
                if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }


    //用一个数据类型为Node 的 数组充当 MyHashMap

    private Node<K, V>[] table;

    transient int modCount;

    //计算key的 hash数值
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

    //初始化MyHashmap
    public MyHashMap() {
        initTable();
    }


    //将数据插入MyHashMap中
    public V put(K key, V value) {
        //如果是空的,则进行初始化大小
        if (table == null) {
            initTable();
        }
        //允许K/V存放null
        //若key为null则调用putForNullKey方法 放在数组第一位置
        if (key == null)
            return putNullKey(value);
        //计算hash

        int hash = hash(key);
        //搜索指定hash值在对应table中的索引。

        int i = indexFor(hash, table.length);

        //如果i处不为空,则循环遍历下一个元素,产生链表
        for (Node<K, V> n = table[i]; n != null; n = n.next) {
            Object k;
            if (n.hash == hash && ((k = n.key) == key || key.equals(k))) {
                V oldValue = n.value;
                n.value = value;
                return oldValue;
            }
        }

        //如果i处索引为null或没有相同的,则表明还没有Entry
        modCount++;
        //插入,将k v插入到i处
        addNode(hash, key, value, i);
        return null;
    }


    void addNode(int hash, K key, V value, int bucketIndex) {

        //若大小不够就扩充到原来的2倍

        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }
        //创建新Node
        createNode(hash, key, value, bucketIndex);
    }

    void createNode(int hash, K key, V value, int bucketIndex) {
        //获取指定索引处的Node
        Node<K, V> n = table[bucketIndex];
        //将新创建的 Node放入 bucketIndex 索引处,并让新的 Entry 指向原来的 Entry
        table[bucketIndex] = new Node<>(hash, key, value, n);
        //增加大小
        size++;
    }


    //初始化table数组
    private void initTable() {

        table = new Node[DEFAULT_INITIAL_CAPACITY];

        //初始化阈值
        threshold=(int) Math.floor(DEFAULT_INITIAL_CAPACITY*DEFAULT_LOAD_FACTOR);
    }

    //只放空数值
    private V putNullKey(V value) {
        Node oldnode = table[0];
        Node node = new Node<K, V>(0, null, value, null);
        table[0] = node;
        return (oldnode == null) ? null : ((V) oldnode.getValue());
    }


    //返回对应key的 在数组的位置 取代模 % 取余运算
    static int indexFor(int h, int length) {
        // length 必须为 2 的N 次
        return h & (length - 1);
    }

    //获取该MyHashMap的size
    public int size() {
        return size;
    }


    //判断该MyHashMap是否为空
    public boolean isEmpty() {
        return size == 0;
    }


    //重新修改起容量  新的容量
    void resize(int newCapacity) {

        //引用扩容前的Node数组
        Node[] oldTable = table;

        int oldCapacity = oldTable.length;

        //扩容前的数组大小如果已经达到最大(2^30)了 1G
        if (oldCapacity == MAXIMUM_CAPACITY) {

            //修改阈值为int的最大值(2^31-1),这样以后就不会扩容了
            threshold = Integer.MAX_VALUE;
            return;
        }

        //初始化一个新的Node数组
        Node[] newTable = new Node[newCapacity];
        //将数据转移到新的Node数组里
        transfer(newTable);

        //将新table 赋值给原来的table
        table = newTable;
        threshold = (int)(newCapacity * DEFAULT_LOAD_FACTOR);//修改阈值
    }



    void transfer(Node[] newTable) {

        //oldtable 引用原来的table
        Node[] oldtable = table;
        int newCapacity = newTable.length;
        //遍历原来的table
        for (int j = 0; j < oldtable.length; j++) {
            //取得旧oldtable数组的每个元素
            Node<K,V> n = oldtable[j];
            if (n != null) {
                //释放我们可爱的oldtable
                oldtable[j] = null;
                do {
                    Node<K,V> next = n.next;
                    //!!重新计算每个元素在数组中的位置
                    int i = indexFor(n.hash, newCapacity);
                    //此处注意 越最近添加的k 对应的节点 其距离 数组越进 可以理解为链表的头插
                    n.next = newTable[i];
                    //将元素放在数组上
                    newTable[i] = n;
                    //访问下一个Node链上的元素
                    n = next;
                } while (n != null);
            }
        }
    }

    //获取数值
    public V get(K key) {
        if (key == null)
            return getForNullKey();
        Node<K,V> node = getNode(key);

        return null == node ? null : node.getValue();
    }


    public Node getNode(K key){

        //如果是空的 则返回null
        if (table == null) {
            return  null;
        }

        int hash = hash(key);
        //搜索指定hash值在对应table中的索引。
        int i = indexFor(hash, table.length);
        //如果i处不为空,则循环遍历下一个元素
        for (Node<K, V> n = table[i]; n != null; n = n.next) {
            Object k;
            if (n.hash == hash && ((k = n.key) == key || key.equals(k))) {
                return n;
            }
        }

        return null;

    }



    //获取key为Null的value
    public V getForNullKey(){
        return  (table[0]==null)?null:(table[0].value);
    }





}