hashmap的擴容因子是0.75 緣由 參考:HashMap默認加載因子爲何選擇0.75?(阿里)html
1. HashMap概述java
HashMap是基於哈希表的Map接口的非同步實現。此實現提供全部可選的映射操做,並容許使用null值和null鍵。此類不保證映射的順序,特別是它不保證該順序恆久不變。面試
2. HashMap的數據結構編程
在java編程語言中,最基本的結構就是兩種,一個是數組,另一個是模擬指針(引用),全部的數據結構均可以用這兩個基本結構來構造的,HashMap也不例外。HashMap其實是一個「鏈表散列」的數據結構,即數組和鏈表的結合體。這樣的結構結合了鏈表在增刪方面的高效和數組在尋址上的優點數組
hashmap結構:哈希表是由數組+鏈表組成的,數組的默認長度爲16 ,注意 hashtable的數組的默認長度是11(能夠自動變長。在構造HashMap的時候也能夠指定一個長度),數組裏每一個元素存儲的是一個鏈表的頭結點。而組成鏈表的結點其實就是hashmap內部定義的一個類:Entity。Entity包含三個元素:key,value和指向下一個Entity的next數據結構
3. HashMap的存取app
這些元素是按照什麼樣的規則存儲到數組中呢。通常狀況是經過hash(key)%(len-1)得到,也就是元素的key的哈希值對數組長度取模獲得。好比上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。因此十二、2八、108以及140都存儲在數組下標爲12的位置。編程語言
HashMap的存儲--put:ide
int hash = key.hashCode(); // 這個hashCode方法這裏不詳述,只要理解每一個key的hash是一個固定的int值 int index = hash %( Entry[].length-1); table[index] = value;//假定存儲鏈表頭結點的數組名爲table
用table[index]表示經過hash值計算出來的、元素須要存儲在數組中的位置。先判斷該位置上有沒有存有Entity,沒有的話就建立一個Entity<k,v>對象,在該位置上插入,插入結束;若是有的話,經過鏈表的遍歷方式去逐個遍歷,經過equals方法將key和已有的key進行比較,看看有沒有已經存在的key,有的話用新的value替換老的value;若是沒有,則在table[index]插入該Entity,把原來在table[index]位置上的Entity賦值給新的 Entity的next,也即,新的Entity插入(put)的位置永遠是在鏈表的最前面(百度面試),這樣插入結束。 函數
打個比方, 第一個鍵值對A進來,經過計算其key的hash獲得的index=0,記作:table[0] = A。一會後又進來一個鍵值對B,經過計算其index也等於0,如今怎麼辦?HashMap會這樣作:B.next = A,table[0] = B,若是又進來C,index也等於0,那麼C.next = B,table[0] = C;這樣咱們發現index=0的地方其實存取了A,B,C三個鍵值對,他們經過next這個屬性連接在一塊兒。
注:null key老是存放在Entry[]數組的第一個元素。
擴展:
按照散列函數的定義,若是兩個對象相同,即obj1.equals(obj2)=true,則它們的hashCode必須相同,但若是兩個對象不一樣,則它們的hashCode不必定不一樣。
若是兩個不一樣對象的hashcode相同,就稱爲衝突。衝突會致使操做哈希表的時間開銷增大,因此儘可能定義好的hashCode()方法,能加快哈希表的操做。覆蓋了equals方法以後必定要覆蓋hashCode方法,緣由很簡單,好比,String a = new String(「abc」);String b = new String(「abc」);若是不覆蓋hashCode的話,那麼a和b的hashCode就會不一樣,把這兩個類當作key存到HashMap中的話就 會出現問題,就會和key的惟一性相矛盾。
HashMap的讀取--get:
先定位到數組元素,再遍歷該元素處的鏈表
public V get(Object key) { if (key == null) return getForNullKey(); int hash = hash(key.hashCode()); //先定位到數組元素,再遍歷該元素處的鏈表 for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value;//顯然,在尋找目標元素的時候,除了對比經過key計算出來的hash值,還會用雙等或equals方法對key自己來進行比較,二者都爲true時纔會返回這個元素 } return null; }
遍歷規則:以下:
import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; public class abc_test { public static void main(String[] args) { // TODO Auto-generated method stub Map<String,String> map=new HashMap<String,String>(); map.put("1", "value1"); map.put("2", "value2"); map.put("3", "value3"); map.put("4", "value4"); //第一種:普通使用,二次取值 System.out.println("\n經過Map.keySet遍歷key和value:"); for(String key:map.keySet()) { System.out.println("Key: "+key+" Value: "+map.get(key)); } //第二種:推薦,尤爲是容量大時 System.out.println("\n經過Map.entrySet遍歷key和value"); for(Map.Entry<String, String> entry: map.entrySet()) { System.out.println("Key: "+ entry.getKey()+ " Value: "+entry.getValue()); } //第三種 System.out.println("\n經過Map.values()遍歷全部的value,但不能遍歷key"); for(String v:map.values()) { System.out.println("The value is "+v); } //第四種 System.out.println("\n經過Iterator 遍歷Map.entrySet的key和value: "); Iterator map1it=map.entrySet().iterator(); while(map1it.hasNext()) { Map.Entry<String, String> entry=(Entry<String, String>) map1it.next(); System.out.println("Key: "+entry.getKey()+" Value: "+entry.getValue()); } //第五種 System.out.println("\n經過iterator 遍歷Map.keySet的key和value: "); Iterator keyIter=map.keySet().iterator(); while(keyIter.hasNext()) { String key=(String)keyIter.next(); System.out.println("Key: "+key+" Value: "+map.get(key)); } } private static void print(Collection<Person> set) { Iterator<Person> it = set.iterator(); while (it.hasNext()) { Person p = it.next(); System.out.println(p.toString()); } } } class test { public int d; public String name; public int getD() { return d; } public void setD(int d) { this.d = d; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object obj) { // TODO Auto-generated method stub return super.equals(obj); } } class Person { public Person(String name, int age) { this.name = name; this.age = age; } private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String toString() { return "{" + name + ", " + age + "}"; } } class joinDemo implements Runnable{ @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<10;i++){ System.out.println("線程1第"+i+"次執行"); } } }
結果爲:
經過Map.keySet遍歷key和value: Key: 1 Value: value1 Key: 2 Value: value2 Key: 3 Value: value3 Key: 4 Value: value4 經過Map.entrySet遍歷key和value Key: 1 Value: value1 Key: 2 Value: value2 Key: 3 Value: value3 Key: 4 Value: value4 經過Map.values()遍歷全部的value,但不能遍歷key The value is value1 The value is value2 The value is value3 The value is value4 經過Iterator 遍歷Map.entrySet的key和value: Key: 1 Value: value1 Key: 2 Value: value2 Key: 3 Value: value3 Key: 4 Value: value4 經過iterator 遍歷Map.keySet的key和value: Key: 1 Value: value1 Key: 2 Value: value2 Key: 3 Value: value3 Key: 4 Value: value4