一、前言
本文由jdk1.8源碼整理而得,附自制jdk1.8底層數據結構圖,並截取部分源碼加以說明結構關系。
二、jdk1.8 HashMap底層數據結構圖
三、源碼
1.散列表(Hash table,也叫哈希表):
/** * 表,第一次使用時初始化(而非實例化集合時進行初始化),並根據需要調整大小。當分配時,長度總是2的冪。(在某些操作中,我們還允許長度為零,以允許當前不需要的引導機制。)
*/ transient Node<K,V>[] table;
2.鏈表:
/** * 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;
…… }
3.紅黑樹:
/** * 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; …… }
四、問題探究
1.散列表后面跟的“鏈表、紅黑樹”是怎么來的,都解決了哪些問題?
答:
①鏈表的由來:Hash碰撞:不同的元素通過hash算法可能會得到相同的hash值,如果都放同一個桶里,后面放進去的就會覆蓋前面放的,所以為了解決hash碰撞時元素被覆蓋的問題,就有了在桶里放鏈表。
②紅黑樹的由來:假設現在HashMap集合中大多數的元素都放到了同一個桶里(由hash值計算而得的桶的位置相同),那么這些元素就在這個桶后面連成了鏈表。現在需要查詢某個元素,那么此時的查詢效率就很慢了,它是在做鏈表查詢( O(N) 的查詢效率)。為了解決這個問題,就引入了紅黑樹( log(n) 的查詢效率):當鏈表到達一定長度時就在鏈表的后面創建紅黑樹。
③其實,“盡量避免hash 沖突,讓元素較為均勻的放置到每個桶”才是查詢效率最高的( O(1) 的查詢效率),這和hash算法的實現息息相關,這里不做深究。
2.如圖可知,散列表后面跟的數據結構有可能是鏈表,也有可能是紅黑樹。散列表后面跟什么數據結構是怎么確定的?
答:
①鏈表節點轉換成紅黑樹節點的閾值, 節點數 >= 8 就轉:
static final int TREEIFY_THRESHOLD = 8;
②紅黑樹節點轉換鏈表節點的閾值, 節點數 <= 6 就轉:
static final int UNTREEIFY_THRESHOLD = 6;
③轉紅黑樹時, table的最小長度:
static final int MIN_TREEIFY_CAPACITY = 64;