HashMap为什么变成从尾部插入?
2019-04-01 本文已影响0人
packet
HashMap的源码比较多,不想粘贴了(也没有必要),这里只粘贴下重要常量。
/**
* 默认初始大小,值为16,要求必须为2的幂
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
* 最大容量,必须不大于2^30
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* 默认加载因子,值为0.75
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* hash冲突默认采用单链表存储,当单链表节点个数大于等于8时,会转化为红黑树存储
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* hash冲突默认采用单链表存储,当单链表节点个数大于8时,会转化为红黑树存储。
* 当红黑树中节点少于等于6时,则转化为单链表存储
*/
static final int UNTREEIFY_THRESHOLD = 6;
/**
* hash冲突默认采用单链表存储,当单链表节点个数大于8时,会转化为红黑树存储。
* 但是有一个前提:要求数组长度大于等于64,否则不会进行转化
*/
static final int MIN_TREEIFY_CAPACITY = 64;
HashMap默认采用数组+单链表方式存储元素,当元素出现哈希冲突时,会存储到该位置的单链表中。但是单链表不会一直增加元素,当元素个数超过8个时,会尝试将单链表转化为红黑树存储。但是在转化前,会再判断一次当前数组的长度,只有数组长度大于等于64才处理。否则,进行扩容操作。此处先提到这,后续会有详细的讲解。
![](https://img.haomeiwen.com/i11772136/624c16c0673b8dc2.png)
![](https://img.haomeiwen.com/i11772136/ec0e204b9ab4f42c.png)
![](https://img.haomeiwen.com/i11772136/cc4fbd63bd440eed.png)
![](https://img.haomeiwen.com/i11772136/5e9eb44065109b29.png)
![](https://img.haomeiwen.com/i11772136/25ba9a913e443e87.png)
![](https://img.haomeiwen.com/i11772136/e3ef8b7c48b212b1.png)
![](https://img.haomeiwen.com/i11772136/2f8196e7dc174cab.png)
![](https://img.haomeiwen.com/i11772136/7fa146ebfaccecd0.png)
![](https://img.haomeiwen.com/i11772136/efc732cf35c6396d.png)
![](https://img.haomeiwen.com/i11772136/9fe302a6c63dd385.png)
![](https://img.haomeiwen.com/i11772136/f3ab2f3c0f4ffd34.png)
HashMap在1.7和1.8之间的变化:
1.7采用数组+单链表,1.8在单链表超过一定长度后改成红黑树存储
1.7扩容时需要重新计算哈希值和索引位置,1.8并不重新计算哈希值,巧妙地采用和扩容后容量进行&操作来计算新的索引位置。
1.7插入元素到单链表中采用头插入法,1.8采用的是尾插入法。
HashMap在jdk1.7中采用头插入法,在扩容时会改变链表中元素原本的顺序,以至于在并发场景下导致链表成环的问题。而在jdk1.8中采用尾插入法,在扩容时会保持链表元素原本的顺序,就不会出现链表成环的问题了。