HashMap源码分析
HashMap的数据结构
1.数组:
2.链表:双向链表
3.红黑树:二叉查找数
源码中验证:
/**
* Holds cached entrySet(). Note that AbstractMap fields are used
* for keySet() and values().
*/
transient Node<K,V>[] table;
/**
* Basic hash bin node, used for most entries. (See below for
* TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
*/
static class Node<K,V> implements Map.Entry<K,V>{
final int hash;
final K key;
V value;
Node<K,V> next;
}
/**
* Entry for Tree bins. Extends LinkedHashMap.Entry (which in turn
* extends Node) so can be used as extension of either regular or
* linked node.
*/
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
}
put操作
1.数组初始化:
通过resize()方法来进行初始化操作:
数组默认大小;
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
newCap = DEFAULT_INITIAL_CAPACITY;
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
2.存储Node节点:
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
3.链表(进行树化)存储:
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
TREEIFY_THRESHOLD :链表转树的判断值;
UNTREEIFY_THRESHOLD:树转链表的判断值;
4.检查数组是否进行扩容:
if (++size > threshold)
resize();
确定数组下标
如何确定数组下标在和位置:
if ((p = tab[i = (n - 1) & hash]) == null)
从这里可以知道数组下标是如何进行确定的;通过hash值与n-1(默认是15)进行与操作;通过二进制进行操作;
数组扩容
数组的扩容操作是在resize()操作中进行扩容;
if (++size > threshold)
resize();
threshold = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
//计算触发扩容值:这里针对默认情况进行计算 16*0.75f;
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
扩容是进行double操作;
数组扩容之后,之前的节点需要进行重新排列:
1.当前节点只有一个节点
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
2.树节点,进行切割,重新插入到数组中:
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
3.编列链表:
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;
}
避免hash冲突
hash记性修改:对key,hashCode的高16位与低16位进行异或运算;
重复几率太大了 --->尽可能避免hash冲突,result结果要尽可能不一样 ---->参与运算的位数只有最后几位?不合理,---->hashCode 32 32利用的淋漓尽致;