hashMap在jdk1.7与jdk1.8中的原理及不同

在分析jdk1.7中HashMap的hash冲突时,不知大家是否有个疑问就是万一发生碰撞的节点非常多怎么版?如果说成百上千个节点在hash时发生碰撞,存储一个链表中,那么如果要查找其中一个节点,那就不可避免的花费O(N)的查找时间,这将是多么大的性能损失。这个问题终于在JDK1.8中得到了解决,在最坏的情况下,链表查找的时间复杂度为O(n),而红黑树一直是O(logn),这样会提高HashMap的效率。

jdk1.7中HashMap采用的是位桶+链表的方式,即我们常说的散列链表的方式,而jdk1.8中采用的是位桶+链表/红黑树的方式,也是非线程安全的。当某个位桶的链表的长度达到某个阀值的时候,这个链表就将转换成红黑树。

jdk1.8中,当同一个hash值的节点数不小于8时,将不再以单链表的形式存储了,会被调整成一颗红黑树(上图中null节点没画)。这就是jdk1.7与jdk1.8中HashMap实现的最大区别。



 
本文链接:https://blog.csdn.net/changhangshi/article/details/82114727

      hashMap为何采用hash表存数据。如果不用hash表,集合中数据是无序的,当我们向集合中添加一个数据时需要同集合中所有的数据进行equals比较,当集合数据比较大时效率是非常的低。因此用hash表存储数据效率非常高。hash表的底层是数组,数组中存的是entry对象,默认长度是16.

      当我们往hash表中添加一个对象时,会调用对象的hash code方法,根据hash算法算出对应的数组的索引值,再根据索引值查找数组,数组中是否存在对象,如果不存在对象直接存进去。

      如果存在对象,则通过equals比较两个对象的key值是否相等,如果相等则覆盖value值。

      如果不相等则形成链表结构,jdk1.7后加的在前面,先加的移下,这种情况叫碰撞。这种碰撞的情况应尽量避免,否存一个索引中链表的数据大量时,该索引当再次插入一个对象时equals比较全部影响效率。这时我们将equals和hashcode方法重写的严谨点,这种还是避免不了,因为数组的索引值有限。因此hashMap提供了加载因子避免碰撞,默认0.75,当元素到达现有的hash表的75%时扩容。一旦扩充就会重新排序hash表,减少碰撞概率。

      但是这两种方法还是避免不了这种碰撞的情况,就会出现查询一个对象可能出现极端情况查询链表的最后一个数据返回,影响查询效率。

     因此jdk1.8改善这种碰撞情况的出现,jdk1.8中的HashMap存储结构是由数组、链表、红黑树这三种数据结构形成,红黑树查询删除快新增慢。存储结构下图所示,根据key的hash与table长度确定table位置,同一个位置的key以链表形式存储,超过一定限制链表转为树。数组的具体存取规则是tab[(n-1) & hash],其中tab为node数组,n为数组的长度,hash为key的hash值。

 1)表中数据的临界值,如果达到8,就进行resize扩展,如果数组大于64则转换为树.

static  final  int TREEIFY_THRESHOLD = 8;

2)如果数组的size大于64,则把链表进行转化为树

static  final  int MIN_TREEIFY_CAPACITY = 64

posted @ 2019-11-13 14:20  valar-dohaeris  阅读(1147)  评论(0编辑  收藏  举报