因為網上已經太多的關於HashMap的相關文章了,為了避免大量重復,又由於網上關於java8的HashMap的相關文章比較少,至少我沒有找到比較詳細的。所以才有了本文。
本文主要的內容:
1.HashMap的數據結構,以及java 8的新特征
2.HashMap的put方法的實現原理
3.resize()到底做了什么事情,它是怎么擴容的
4.HashMap節點紅黑樹存儲
**HashMap的數據結構,以及java 8的新特征** 下面來看下HashMap的主要兩種存儲方式是示意圖(圖片來自網絡):  這就是java8的HashMap的數據結構,跟之前版本不一樣的是當table達到一定的閥值時,bucket就會由鏈表轉換為紅黑樹的方式進行存儲,下面會做具體的源碼分析。
**HashMap的put方法實現原理** 下面我們來看下關於put的方法,hashMap的Capacity的默認值為16,負載因子的默認值為0.75 ```java final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node
因為已經做了注釋了具體請看注釋,所以大部分細節就不多說了,下面說說hash的算法和尋址的算法
首先計算hash值 ```java static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } ```
具體的計算過程用如下圖表示,在設計hash函數時,因為目前的table長度2的N次方,而計算下標的時候,使用&位操作,而非%求余: 
請看putVal代碼27/28行,當桶bucket大於TREEIFY_THRESHOLD(8)值時就執行treeifyBin,如果是之前java7之前的代碼的話是要進行擴容的,但是java8可能會把這個bucket的鏈表上的數據轉化為紅黑樹 ```java final void treeifyBin(Node
resize到底做了什么事情,它是怎么擴容的
我們先看下resize這個方法吧,這段代碼后面會講到24行的treeify方法,也是本文的重點紅黑樹的存儲,以為這個方法的實現方式還是有別與java7的,桶中存在一個鏈表,需要將鏈表重新整理到新表當中,因為newCap是oldCap的兩倍所以原節點的索引值要么和原來一樣,要么就是原(索引+oldCap)和JDK 1.7中實現不同這里不存在rehash
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 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;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(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, 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;
//把節點移動新的位置j+oldCap,這種情況不適用與鏈表的節點數大於8的情況
//鏈表節點大於8的情況會轉換為紅黑樹存儲
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
**HashMap節點紅黑樹存儲** 好了終於到treeify了,大部分內容都在注解中 ```java final void treeify(Node
當insert一個節點之后為了達到平衡,我們可能需要對節點進行旋轉和顏色翻轉(上面的balanceInsertion方法)。具體操作這里就不細講了,對紅黑樹的修復還不是很清楚的同學可以去參考下數據結構與算法分析這本書,我將在后面寫一篇關於紅黑樹關於java實現的相關文章。
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
TreeNode<K,V> x) {
//插入的節點必須是紅色的,除非是根節點
x.red = true;
//遍歷到x節點為黑色,整個過程是一個上濾的過程
//xp=x.parent;xpp=xp.parent;xppl=xpp.left;xppr=xpp.right;
for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
if ((xp = x.parent) == null) {
x.red = false;
return x;
}
//如果xp的黑色就直接完成,最簡單的情況
else if (!xp.red || (xpp = xp.parent) == null)
return root;
//如果x的父節點是x父節點的左節點
if (xp == (xppl = xpp.left)) {
//x的父親節點的兄弟是紅色的(需要顏色翻轉)
if ((xppr = xpp.right) != null && xppr.red) {
//x父親節點的兄弟節點置成黑色
xppr.red = false;
//父幾點和其兄弟節點一樣是黑色
xp.red = false;
//祖父節點置成紅色
xpp.red = true;
//然后上濾(就是不斷的重復上面的操作)
x = xpp;
}
else {
//如果x是xp的右節點整個要進行兩次旋轉,先左旋轉再右旋轉
if (x == xp.right) {
root = rotateLeft(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
root = rotateRight(root, xpp);
}
}
}
}
//以左節點鏡像對稱就不做具體分析了
else {
if (xppl != null && xppl.red) {
xppl.red = false;
xp.red = false;
xpp.red = true;
x = xpp;
}
else {
if (x == xp.left) {
root = rotateRight(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
root = rotateLeft(root, xpp);
}
}
}
}
}
}
參考