HashMap源码分析

Posted 技术无产者

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HashMap源码分析相关的知识,希望对你有一定的参考价值。

JDK1.7和jdk1.8对于HashMap实现的异同总结:

具体可以看这篇文章,但这篇文章几个地方存在歧义,下面做以补充:

(1)美团面试题:Hashmap的结构,1.7和1.8有哪些区别,史上最深入的分析_王伟的博客-CSDN博客_hashmap1.7和1.8的区别(一) 真实面试题之:Hashmap的结构,1.7和1.8有哪些区别不同点:(1)JDK1.7用的是头插法,而JDK1.8及之后使用的都是尾插法,那么他们为什么要这样做呢?因为JDK1.7是用单链表进行的纵向延伸,当采用头插法就是能够提高插入的效率,但是也会容易出现逆序且环形链表死循环问题。但是在JDK1.8之后是因为加入了红黑树使用尾插法,能够避免出现逆序且链表死循环的问题。(2)扩...https://blog.csdn.net/qq_36520235/article/details/82417949

1.扰动函数hash:

jdk1.7和jdk1.8 中的hash函数不同,相⽐于 JDK1.8 hash ⽅法 ,JDK 1.7 hash ⽅法的性能会稍差⼀点点,因为毕竟扰动了 4 次,而jdk1.8中的hash扰动了4次。

2.JDK1.7的无效扩容问题:

来自评论区大佬:

void addEntry(int hash, K key, V value, int bucketIndex) 
        if ((size >= threshold) && (null != table[bucketIndex])) 
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        
 
        createEntry(hash, key, value, bucketIndex);
    

这是1.7中的hashmap添加元素的源代码,可以看到他是先判断size 》= threshold值 并且 当前数组索引的桶不为空就扩容。这样存在当插入的key值与桶中某一key一致时,是替换value而不是插入新的节点,这样的size值是保持不变,没有大于threshold,本来不需要进行扩容但1.7源代码进行了扩容。而在1.8源代码中,扩容的前提是一定添加了新的node节点(++size > threshold)所以1.8中不会出现无效扩容的问题(1.8中若有value替换的情况是直接返回了,不会到扩容这一步)

简单来说: 当table的size到达阈值,这时候将插入链表中已经存在的Key时,由于 if ((size >= threshold) && (null != table[bucketIndex])) 为true,会直接触发扩容,从而导致无效扩容。

3.为什么 HashMap 中 String、Integer 这样的包装类适合作为 key 键

来自评论区:

1.hashcode不可变;

2.String内部会维护一个hash缓存,在第一次使用时才会计算,从而避免每次计算;

3.对于Integer的hashcode是返回他自身的int值,重复概率高,并且低位特性不随机,所以不推荐使用Integer类型.

结论: 不是所有的包装类都适合作为key;String更适合. 其中包装类以自身为值: byte、integer、short、boolean(固定值) 不以自身为值:long、double、char

4.HashMap 中的 key若 Object类型, 则需实现哪些方法?

Object类型若不重写hashcode方法,则默认调用的是Object的hashcode,很可能会出现严重的hash碰撞问题

5.Jdk1.7和1.8 hashmap的异同:

来自评论区:

  1. jdk7 数组+单链表 jdk8 数组+(单链表+红黑树)

  2. jdk7 链表头插 jdk8 链表尾插

  3. 头插: resize后transfer数据时不需要遍历链表到尾部再插入

  4. 头插: 最近put的可能等下就被get,头插遍历到链表头就匹配到了

  5. 头插: resize后链表可能倒序; 并发resize可能产生循环链

  6. jdk7 先扩容再put jdk8 先put再扩容 (why?有什么区别吗?)

  7. jdk7 计算hash运算多 jdk8 计算hash运算少(http://www.jasongj.com/java/concurrenthashmap/#寻址方式-1)

  8. jdk7 受rehash影响 jdk8 调整后是(原位置)or(原位置+旧容量)

6.fail-fast 并不是在多线程下才会被触发,具体可以看我的另一篇博客:

fail-fast 机制_zyz的博客-CSDN博客在项目中,写的一段程序为对Set集合遍历的过程中异常set集合里的元素,导致出现下面的异常:Set<String> keySet=disposableBeansMap.keySet ();keySet.toArray ();for (String key:keySet)try //从单例缓存中清除beansingletonObjectMap.remove (key);//从销毁缓存中取出Bean对应的销毁函数Disposablehttps://blog.csdn.net/qq_39552268/article/details/121113813

以上是关于HashMap源码分析的主要内容,如果未能解决你的问题,请参考以下文章

JDK源码阅读之 HashMap

HashMap实现原理和源码详细分析

HashMap实现原理和源码详细分析

LinkedHashMap 源码分析

最通俗易懂的 HashMap 源码分析解读

JDK1.7 HashMap 源码分析