java--HashMap数据结构( 四 )


我们分析下的源码 , 鉴于JDK1.8融入了红黑树 , 较复杂 , 为了便于理解我们仍然使用JDK1.7的代码 , 好理解一些 , 本质上区别不大
这里就是使用一个容量更大的数组来代替已有的容量小的数组 , ()方法将原有Entry数组的元素拷贝到新的Entry数组里 。
[i]的引用赋给了e.next , 也就是使用了单链表的头插入方式 , 同一位置上新元素总会被放在链表的头部位置;这样先放在一个索引上的元素终会被放到Entry链的尾部(如果发生了hash冲突的话) , 这一点和Jdk1.8有区别 , 下文详解 。在旧数组中同一条Entry链上的元素 , 通过重新计算索引位置后 , 有可能被放到了新数组的不同位置上 。
下面举个例子说明下扩容过程 。假设了我们的hash算法就是简单的用key mod 一下表的大小(也就是数组的长度) 。其中的哈希桶数组table的size=2 ,  所以key = 3、7、5 , put顺序依次为 5、7、3 。在mod 2以后都冲突在table[1]这里了 。这里假设负载因子 =1 , 即当键值对的实际大小size 大于 table的实际大小时进行扩容 。接下来的三个步骤是哈希桶数组 成4 , 然后所有的Node重新的过程 。
java8优化点:不再重新计算hash值 , 的过程均匀的把之前的冲突的节点分散到新的了 。这一块就是JDK1.8新增的优化点 。有一点注意区别 , JDK1.7中的时候 , 旧链表迁移新链表的时候 , 如果在新表的数组索引位置相同 , 则链表元素会倒置 , 但是从上图可以看出 , JDK1.8不会倒置 。有兴趣的同学可以研究下JDK1.8的源码 , 写的很赞
小结
(1) 扩容是一个特别耗性能的操作 , 所以当程序员在使用的时候 , 估算map的大小 , 初始化的时候给一个大致的数值 , 避免map进行频繁的扩容 。
(2) 负载因子是可以修改的 , 也可以大于1 , 但是建议不要轻易修改 , 除非情况非常特殊 。
(3) 是线程不安全的 , 不要在并发的环境中同时操作 , 建议使用 。
(4) JDK1.8引入红黑树大程度优化了的性能 。
(5) 还没升级JDK1.8的 , 现在开始升级吧 。的性能提升仅仅是JDK1.8的冰山一角 。