那只是一股逆流

resize()方法

数组长度的技巧

1. 通过与元素的hash值进行与操作，能够快速定位到数组下标 相对于取模运算，直接进行与操作能提高计算效率。在CPU中，所有的加减乘除都是通过加法实现的，而与操作时CPU直接支持的。
2. 扩容时简化计算数组下标的计算量 因为数组每次扩容都是原来的两倍，所以每一个元素在新数组中的位置要么是原来的index，要么index = index + oldCap，假设

``````//如果扩容后，元素的index依然与原来一样，那么使用这个head和tail指针
Node<K,V> loHead = null, loTail = null
Node<K,V> hiHead = null, hiTail = null
Node<K,V> next;
do {
next = e.next;
//这个地方直接通过hash值与oldCap进行与操作得出元素在新数组的index
if ((e.hash & oldCap) == 0) {
if (loTail == null)
else
loTail.next = e;
//tail指针往后移动一位，维持顺序
loTail = e;
}
else {
if (hiTail == null)
else
hiTail.next = e;
//tail指针往后移动一位，维持顺序
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
//还是原来的index
}
if (hiTail != null) {
hiTail.next = null;
//index = index + oldCap
}
``````

resize()方法完整代码

``````
/**
* Initializes or doubles table size.  If null, allocates in
* accord with initial capacity target held in field threshold.
* Otherwise, because we are using power-of-two expansion, the
* elements from each bin must either stay at same index, or move
* with a power of two offset in the new table.
*
* @return the table
*/
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in thresh
newCap = oldThr;
else {               // zero initial threshold signifies usin
newCap = DEFAULT_INITIAL_CAPACITY;
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMU
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, old
else { // preserve order  保持顺序
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
//这个地方很巧妙，直接通过e.hash & oldCap得出e在newTab中的位置；
//因为table是2倍扩容，所以只需要看hash值与oldCap进行与操作，结果为0，那么还是原来的index；否则index = index + oldCap
if ((e.hash & oldCap) == 0) {
if (loTail == null)
else
//新的节点放在末尾
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
else
//新的节点放在末尾
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
//还是原来的index
}
if (hiTail != null) {
hiTail.next = null;
//index 变为 index + oldCap
}
}
}
}
}
return newTab;
}

``````

附加题

JDK 1.8 是否会出现类似于 JDK 1.7中那样的死循环呢？ 我感觉有可能也会出现类似问题。 今下午想了很久，最后还是没有得到一个清晰的答案； 以后有了答案再补回来～

参考资料

那只是一股逆流

JDK1.8 不一样的HashMap

01/05
0
0
HashMap在JDK1.8中并发操作，代码测试以及源码分析

HashMap在JDK1.8中并发操作不会出现死循环，只会出现缺数据。测试如下： 1 package JDKSource; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 import java.util.concurrent.Tim......

wenbochang
08/05
0
0
Java 8系列之重新认识HashMap

2017/11/05
0
0
Java 深入分析HashMap

11/14
0
0
Java HashMap的工作原理 及各种Map区别

2015/03/30
0
0

Spring源码学习笔记-1-Resource

zypy333

10
0
RestClientUtil和ConfigRestClientUtil区别说明

RestClientUtil directly executes the DSL defined in the code. ConfigRestClientUtil gets the DSL defined in the configuration file by the DSL name and executes it. RestClientUtil......

bboss

17
0

2
0
Linux系统设置全局的默认网络代理

11
0
java框架学习日志-6（bean作用域和自动装配）

10
0