HashMap总结归纳

1.HashMap底层是如何实现的?

JDK1.7:数组+链表

JDK1.8:  数组+链表/红黑树


2.HashMap的长度为什么是2的幂

 1.8和1.7对比之后发现都是2的幂,因为对key进行hash运算之后,将key映射到哈希桶数组位置,需要取模运算hash%length,而如果是2的幂,hash & (lenth-1)正好等同于hash % length,但是位运算比取模效率更好。

1.8:
 if ((p = tab[i = (n - 1) & hash]) == null)
      tab[i] = newNode(hash, key, value, null);
1.7:
 static int indexFor(int h, int length) {
    return h & (length-1);
 }

3、JDK1.8计算hash为何采用hashcode与向低位移动16位再按位异或

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

因为如果高位16位不同,低位16位相同,计算下标的时候会相同,所以避免分布不均匀,jdk1.8采用这种方式使得key的hashcode尽量不同,分散的均匀。


4.Jdk1.8中满足什么条件后将链表转化成红黑树?
在putVal方法中是判断桶内的某一index挂的节点个数是否大于8,大于则转为红黑树。一旦转换成红黑树,即时以后删除数据也不会还原成链表,至于为何是8,是因为根据泊松分布,在负载因为为0.75的情况下,单个hash槽内节点下面的元素个数为8的概率小于千万分之一,所以将7作为一个分水岭,等于7不转换,大于等于8才进行转换。

5.>>> JDK1.7链表使用头插法,为啥JDK1.8改成了尾插法,因为多线程同时扩容(rehash)情况下,jdk1.7 resize 头插法会容易出现死循环,所以改成尾插法,一定程度上避免了死循环;

 >>> 并发情况下最好不要使用hashmap,因为即使jdk版本>=1.8也会出现其他原因的死循环,建议使用并发容器ConcurrentHashMap。

 

6、 HashMap就是使用哈希表来存储的。哈希表为解决冲突,可以采用开放地址法和链地址法等来解决问题,Java中HashMap采用了链地址法

 

7、在扩充HashMap的时候,不需要像JDK1.7的实现那样重新计算hash,只需要看看原来的hash值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“原索引+oldCap”

jdk7:
    #这里拿新容量重新计算了一次
    int i = indexFor(e.hash, newCapacity);
    
    static int indexFor(int h, int length) {
        return h & (length-1);
    }
jdk8:
  #这里直接和原来的cap按位与原来的cap,看看新增的bit是0还是1
if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; }

 

posted @ 2020-05-31 19:49  feibazhf  阅读(157)  评论(0)    收藏  举报