[Java 8 HashMap 详解系列] 文章目录
1.HashMap 的存储数据结构
2.HashMap 中 Key 的 index 是怎样计算的?
3.HashMap 的 put() 方法执行原理
4.HashMap 的 get() 方法执行原理
5.HashMap 的 remove() 方法执行原理
6.HashMap 的扩容 resize() 原理
7.HashMap 中的红黑树原理
6.HashMap 的扩容 resize() 原理
我们先来上一段测试代码,直观感受一下 HashMap 的真实的扩容过程:
package i
import java.util.*
/**
* @author: Jack
* 2020-03-21 21:55
*/
fun main(args: Array<String>) {
val map = HashMap<String, Int>(2)
val clz = map::class.java
val capacity = clz.getDeclaredMethod("capacity")
capacity.isAccessible = true
val size = clz.getDeclaredMethod("size")
size.isAccessible = true
println("capacity=${capacity.invoke(map)}")
println("size=${size.invoke(map)}")
println(map)
map["a"] = 1
println("capacity=${capacity.invoke(map)}")
println("size=${size.invoke(map)}")
println(map)
map["b"] = 2
println("capacity=${capacity.invoke(map)}")
println("size=${size.invoke(map)}")
println(map)
map["c"] = 3
println("capacity=${capacity.invoke(map)}")
println("size=${size.invoke(map)}")
println(map)
map["ab"] = 12
println("capacity=${capacity.invoke(map)}")
println("size=${size.invoke(map)}")
println(map)
map["bc"] = 23
println("capacity=${capacity.invoke(map)}")
println("size=${size.invoke(map)}")
println(map)
map["abc"] = 123
println("capacity=${capacity.invoke(map)}")
println("size=${size.invoke(map)}")
println(map)
map["abcd"] = 1234
println("capacity=${capacity.invoke(map)}")
println("size=${size.invoke(map)}")
println(map)
}
输出:
capacity=2
size=0
{}
capacity=2
size=1
{a=1}
capacity=4
size=2
{a=1, b=2}
capacity=4
size=3
{a=1, b=2, c=3}
capacity=8
size=4
{a=1, ab=12, b=2, c=3}
capacity=8
size=5
{a=1, ab=12, bc=23, b=2, c=3}
capacity=8
size=6
{a=1, ab=12, bc=23, b=2, c=3, abc=123}
capacity=16
size=7
{a=1, ab=12, bc=23, b=2, c=3, abc=123, abcd=1234}
源码分析
/**
* 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;
}
// 新容量 = oldCap << 1
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 threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
// 新的阈值 = 0.75 * newCap
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
// 新创建一个 Node<K,V>[] 数组, 容量是原来的 2 倍
@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, oldCap);
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;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
Kotlin 开发者社区
国内第一Kotlin 开发者社区公众号,主要分享、交流 Kotlin 编程语言、Spring Boot、Android、React.js/Node.js、函数式编程、编程思想等相关主题。
越是喧嚣的世界,越需要宁静的思考。