HashMap

73 阅读3分钟

HashMap

1.键值对存储,允许为空,key值唯一,如果相同就覆盖

2.线程不安全

3.底层是hash表,不是有序的

4.使用数组加链表加红黑树存储

插入过程(put)

1.调用自己的hash方法

(n - 1) & hash//使用与运算的方式快速取模
    //16 - 1 	 1111(15) 
    //hash = 6	 0110
    //&			 0110(6)
    //hash = 18 10010
    //&			 0010(2)
    //使用与运算效率更高,n数组的长度必须是2的幂次方
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

2.如果数组为空,调用resize()方法初始化数组

if ((tab = table) == null || (n = tab.length) == 0)
    n = (tab = resize()).length;

3.通过hash方法计算到的hash值,与数组长度减一取与运算,得到数组下标,如果数组位置为空,直接添加到当前位置

if ((p = tab[i = (n - 1) & hash]) == null)
    tab[i] = newNode(hash, key, value, null);

4.不为空,出现hash碰撞

if (p.hash == hash &&
    ((k = p.key) == key || (key != null && key.equals(k))))
    //1.p.hash == hash哈希值相等
    //2.(k = p.key) == key地址比较相等
    //3.key != null && key.equals(k)重写hash方法

4.1.如果key相同,覆盖旧值

e = p;

4.2.如果是红黑树结构,调用红黑树的插入方法

e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

4.3.如果是链表结构,循环遍历直到某个节点为空,或者找到相同的key覆盖,判断是否要转换为红黑树

for (int binCount = 0; ; ++binCount) {
    if ((e = p.next) == null) {
        p.next = newNode(hash, key, value, null);
        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
            treeifyBin(tab, hash);
        break;
    }
    if (e.hash == hash &&
        ((k = e.key) == key || (key != null && key.equals(k))))
        break;
    p = e;
}

4.4.如果是覆盖了hashmap的某个值,或者,返回旧值

if (e != null) { // existing mapping for key
    V oldValue = e.value;
    if (!onlyIfAbsent || oldValue == null)
        e.value = value;
    afterNodeAccess(e);
    return oldValue;
}

4.5.如果是在数组上的添加,判断是否需要扩容

if (++size > threshold)
    resize();

获取过程(get)

1.调用自己的hash函数然后通过运算得到桶的位置,如果与桶的首位kay相同就直接放回值,否则遍历红黑树或者链表找到相同的key值并且返回对应的值

扩容过程

场景:

1.初始化数组

2.链表长度大于8,数组小于64

3.达到阈值时

扩容:

1.初始化默认长度16,创建长度为16的数组返回,正常扩容原来长度的两倍

else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
         oldCap >= DEFAULT_INITIAL_CAPACITY)
    newThr = oldThr << 1; // double threshold

2.桶里面只有一个元素的,与新的容量-1取与运算得到新的桶的位置下标,然后放入

3.桶的结构是红黑树

4.链表 遍历链表的每一个节点,如果该节点与旧的数组长度取与为零,则表明这个节点还在当前位置上,如果不为零就在当前位置加扩容的长度上,构建链表并插入。

//4 100
//8 1000
hash = 3 011 & 100 = 0
		 011 & 111 = 3
hash = 7 111 & 100 = 7
		 111 & 111 = 7 == 3(数组位置) + 4( )