在java業界,有一些問題在面試的時候能夠說基本上是必問的。好比:java中HashMap、HashTable、ConcurrentHashMap的區別,又或者是ArrayList 和 LinkedList 和 Vector 的區別。java
而這些問題,我總結一下,基本上都是java中的數據結構問題,咱們對這些數據結構的瞭解是很是淺薄的,好比說在1.8JDk版本先後的這些數據結構的變化是很是大的,可是咱們對這些卻依然不是很瞭解,不深刻進去,很難明白到底發生了什麼。那麼,接下來的一系列博客,都讓咱們來分析一下java中的數據結構吧。面試
今天,咱們先了解的是Hashmap。數據結構
ps : 我如今的jdk版本是1.8。函數
在JDK1.6,JDK1.7中,HashMap採用位桶+鏈表實現,即便用鏈表處理衝突,同一hash值的鏈表都存儲在一個鏈表裏。可是當位於一個桶中的元素較多,即hash值相等的元素較多時,經過key值依次查找的效率較低。this
而JDK1.8中,HashMap採用位桶+鏈表+紅黑樹實現,當鏈表長度超過閾值時,將鏈表轉換爲紅黑樹,這樣大大減小了查找時間。spa
先來看HashMap裏面的一些參數: code
//默認初始大小是16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
**************************************************************************************
//設置的最大容量是2的30次方
static final int MAXIMUM_CAPACITY = 1 << 30;
**************************************************************************************
//設置的默認加載因子爲0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;
**************************************************************************************
//當某個桶節點數量大於8時,會轉換爲紅黑樹。
static final int TREEIFY_THRESHOLD = 8;
**************************************************************************************
//當某個桶節點數量小於6時,會轉換爲鏈表,前提是它當前是紅黑樹結構。
static final int UNTREEIFY_THRESHOLD = 6;
**************************************************************************************
//當整個hashMap中元素數量大於64時,也會進行轉爲紅黑樹結構。
static final int MIN_TREEIFY_CAPACITY = 64;
*************************************************************************************
//也是加載因子,只不過這個是變量。
final float loadFactor;
*************************************************************************************
//臨界值,也就是元素數量達到臨界值時,會進行擴容。
int threshold;
複製代碼
PS:HashMap默認的加載因子是0.75,最大容量是16,所以能夠得出HashMap的默認容量是:0.75*16=12。cdn
加載因子是表示Hsah表中元素的填滿的程度.若:加載因子越大,填滿的元素越多,好處是,空間利用率高了,但:衝突的機會加大了.反之,加載因子越小,填滿的元素越少,好處是衝突的機會減少了,但空間浪費多了。blog
看了HashMap的一些參數,如今咱們來了解一下HashMap的構造函數:ci
構造函數1
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // 設置加載因子爲默認值0.75
}
複製代碼
構造函數2
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR); //指向構造函數3,且加載因子爲默認的0.75
}
複製代碼
構造函數3
public HashMap(int initialCapacity, float loadFactor) {
//判斷初始加載容量是否小於0,如果,直接拋出異常
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
//判斷初始加載容量是否大於最大容量,如果,設置初始加載容量爲最大容量
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
//判斷加載因子是否小於0或者判斷是否爲NaN,如果,拋出異常
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +loadFactor);
//設置初始加載因子
this.loadFactor = loadFactor;
//對臨界值進行初始化,tableSizeFor(t)這個方法會返回大於t值的,且離其最近的2次冪,例如t爲29,則返回的值是32
this.threshold = tableSizeFor(initialCapacity);
}
複製代碼
PS:NaN 實際上就是 Not a Number的簡稱。0.0f/0.0f的值就是NaN,從數學角度說,0/0就是一種未肯定。
構造函數4
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR; //設置加載因子爲默認的0.75
putMapEntries(m, false);
}
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
//獲取該map的實際長度
int s = m.size();
if (s > 0) {
//判斷table是否初始化,若是沒有初始化
if (table == null) { // pre-size
//求出須要的容量,由於實際使用的長度=容量*0.75得來的,+1是由於小數相除,基本都不會是整數,容量大小不能爲小數的,後面轉換爲int,多餘的小數就要被丟掉,因此+1,例如,map實際長度22,22/0.75=29.3,所須要的容量確定爲30,有人會問若是剛恰好除得整數呢,除得整數的話,容量大小多1也沒什麼影響
float ft = ((float)s / loadFactor) + 1.0F;
//判斷該容量大小是否超出上限。
int t = ((ft < (float)MAXIMUM_CAPACITY) ?
(int)ft : MAXIMUM_CAPACITY);
//對臨界值進行初始化,tableSizeFor(t)這個方法會返回大於t值的,且離其最近的2次冪,例如t爲29,則返回的值是32
if (t > threshold)
threshold = tableSizeFor(t);
}
//若是table已經初始化,且map的實際長度大於臨界值,則進行擴容操做,resize()就是擴容。
else if (s > threshold)
resize();
//遍歷,把map中的數據轉到hashMap中。
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
K key = e.getKey();
V value = e.getValue();
putVal(hash(key), key, value, false, evict);
}
}
複製代碼
HashMap的擴容方法很是複雜,等有機會了咱們再來分析,咱們只要明白在HashMap中的一些參數,就大概能明白HashMap的擴容方法的原理。
//當某個桶節點數量大於8時,會轉換爲紅黑樹。
static final int TREEIFY_THRESHOLD = 8;
**************************************************************************************
//當某個桶節點數量小於6時,會轉換爲鏈表,前提是它當前是紅黑樹結構。
static final int UNTREEIFY_THRESHOLD = 6;
**************************************************************************************
//當整個hashMap中元素數量大於64時,也會進行轉爲紅黑樹結構。
static final int MIN_TREEIFY_CAPACITY = 64;
複製代碼
JDK1.8中,HashMap採用位桶+鏈表+紅黑樹實現,當鏈表長度超過閾值時,將鏈表轉換爲紅黑樹,所以,在HashMap的擴容方法中,不單單是容量的變化,並且也有着數據結構本質上的變化。
咱們發現,java中的HashMap是在與時俱進,變化一直都在。
還有一些關於HashMap的put和get方法,咱們在這裏沒有進行分析,可是因爲本人的技術還不夠,對於紅黑樹的不熟悉,所以不能深刻解析,但願之後在瞭解了紅黑樹之後,可以回過頭來更深刻的瞭解HashMap。