Hashmap1.7和1.8 主要有四个区别,下面将一一说明
存储结构
在1.7版本中,HashMap使用数组+链表的方式实现,即当发生哈希冲突时,会使用链表将冲突的元素串起来。 在1.8版本中,当链表长度超过一定阈值(默认为8)时,链表会转化为红黑树,以提高查找效率。
并发性
在1.7版本中,HashMap在多线程环境下存在并发问题,可能导致死循环。 在1.8版本中,HashMap引入了"锁分段"机制,将整个存储空间分成了多个段(默认为16段),每个段独立加锁,可以提高并发性能。
扩容机制
在1.7版本中,HashMap的扩容机制是当元素个数超过容量的75%时进行扩容,扩容后容量会翻倍,把所有元素重新计算一遍位置,为了降低hash冲突。 在1.8版本中,扩容机制进行了优化,当链表长度超过一定阈值(默认为8)且元素个数超过容量的75%时,会进行链表转换为红黑树的操作,并且扩容时不再翻倍,而是以原来容量的两倍进行扩容,扩容后,不会把所有元素重新计算一遍位置。
PUT插入方式
JDK1.7用的是头插法,而JDK1.8及之后使用的都是尾插法,因为JDK1.7是用单链表进行的纵向延伸,当采用头插法时会容易出现逆序且环形链表死循环问题。但是在JDK1.8之后是因为加入了红黑树使用尾插法,能够避免出现逆序且链表死循环的问题。
相关问题
- 为什么在JDK1.7的时候是先进行扩容后进行插入,而在JDK1.8的时候则是先插入后进行扩容的呢?
其实就是当这个Map中实际插入的键值对的值的大小如果大于这个默认的阈值的时候(初始是16*0.75=12)的时候才会触发扩容 - 为什么在JDK1.8中进行对HashMap优化的时候,把链表转化为红黑树的阈值是8,而不是7或者不是20呢?
如果选择6和8(如果链表小于等于6树还原转为链表,大于等于8转为树),中间有个差值7可以有效防止链表和树频繁转换。假设一下,如果设计成链表个数超过8则链表转换成树结构,链表个数小于8则树结构转换成链表,如果一个HashMap不停的插入、删除元素,链表个数在8左右徘徊,就会频繁的发生树转链表、链表转树,效率会很低。
还有一点重要的就是由于treenodes的大小大约是常规节点的两倍,因此我们仅在容器包含足够的节点以保证使用时才使用它们,当它们变得太小(由于移除或调整大小)时,它们会被转换回普通的node节点,容器中节点分布在hash桶中的频率遵循泊松分布,桶的长度超过8的概率非常非常小。所以作者应该是根据概率统计而选择了8作为阀值