知識鋪: 致力於打造輕知識點,持續更新每次的知識點較少,閱讀不累。不佔太多時間,不停地來喚醒你記憶深處的知識點。
一、認識HashMap
Java中的HashMap散列存儲。
1.1 是一種數據結構,它允許我們存儲對象並在我們知道key的情況下在恆定時間O(1)中檢索它。
1.2 在散列中,散列函數用於連接HashMap中的鍵和值。通過調用HashMap的put(key,value)方法存儲對象,並通過調用get(key)方法檢索對象。當我們調用put方法時,調用key對象的hashcode()方法,以便map的hash函數可以找到存儲value對象的位置,這實際上是內部數組的索引,稱為索引表。
1.3 HashMap在內部以Map.Entry對象的形式存儲映射,該對象包含鍵和值對象。如果要檢索對象,請調用get()方法並再次傳遞key對象。這一次,key對象生成相同的hashCode。這裡需要注意的是,如果key是自定義變量,需要實現hashCode 和equalsequals方法。 一般使用的是String作為可以,這是由於String實現了自己的hashCode 和方法,只有這樣保證計算出來的索引最終會在同一個桶位置。
1.4 HashMap的內部數組是固定大小的,並且如果你繼續存儲對象,在某些時候hash函數將為兩個不同的鍵返回相同的索引位置,這在HashMap中稱為碰撞。在這種情況下,在該桶位置處形成鍊表,並且將新Entry存儲為下一節點。
1.5 從此鍊表中檢索對象,我們需要額外檢查以搜索正確的值,這是通過equals()方法完成的。由於每個節點都包含一個Entry,HashMap使用equals()保持比較Entry的key對象和傳遞的key,當它返回true時,Map返回相應的值。
1.6 搜索內聯列表是O(n)操作,因此在最壞的情況下,哈希衝突將映射減少到鍊表。最近在Java 8中通過將鍊表替換為樹以在O(logN)時間內搜索來解決此問題。
二、Hashtable和HashMap之間的區別
2.1 HashMap 特點
HashMap 允許 null作為key , 線程不安全, 查詢key時間複雜度 O(1)
2.2 HashMap 的 put 具體操作
HashMap實現調用hashCode方法在Key對象上,將返回的hashcode應用到自己的散列函數中,找到用於存儲Entry對象的存儲桶位置,檢測hashCode碰撞,然後繼續在該桶的位置維護一個鍊表,將該value對應的Entry對象進行存儲下一個節點上。
2.3 HashMap 的 hashCode碰撞
不同的key進行hashcode計算後,然後進行索引定位的時候,容易發生碰撞。這是由於我們的hashMap中的桶個數是一定的,計算的code值進行長度取模的時候,落到同一個索引桶中。
如圖:get方法如何在Java中的HashMap中工作
a. 先計算出 key 的hashcode對應的索引桶位置
b. 索引桶找到後,在遍歷鍊表,進行key,value比較
2.4 為什麼String,Integer和其他包裝類被認為是好的鍵?
String ,Integer 和其他包裝類是HashMap 鍵的自然候選者,String 也是最常用的鍵,因為String是不可變的和final的,並且會覆蓋equals 和hashcode()方法。其他包裝類也共享類似的屬性。不可變性是必需的,以防止用於計算hashCode()的欄位發生變化,因為如果key對象在插入和檢索期間返回不同的hashCode,則無法從HashMap 獲取對象。 不可變性是最好的。
2.5 可以在HashMap中使用任何自定義對象作為鍵
Java HashMap中使用任何Object 作為鍵,前提是它覆蓋equals和hashCode方法,並且一旦將對象插入Map,其hashCode就不應該變化。
2.6 使用ConcurrentHashMap代替Hashtable
Hashtable 是同步的,但是ConcurrentHashMap 只通過鎖定分級桶來提供更好的並發性。ConcurrentHashMap 肯定是作為Hashtable 引入的 ,可以代替它使用,但Hashtable 提供比ConcurrentHashMap更強的線程安全性。
三、總結HashMap
3.1 HashMap的工作原理是散列,我們有put()和get()方法來存儲和檢索來自HashMap的對象。當我們將key和value傳遞給put()方法存儲在HashMap上時,它使用key對象hashcode()通過對該哈希值來計算索引位置。檢索它時使用key對象equals方法來找出與該key關聯的正確key值對和返回值對象。HashMap在發生衝突時使用鍊表,對象將存儲在鍊表的下一個節點中。 此外,HashMap以Map.Entry對象的形式在鍵列表的每個節點中存儲鍵元組和值元組。
3.2 HashMap的hashcode碰撞,它們將存儲在同一個存儲桶中。
3.3 如何在HashMap中處理null鍵?由於equals()和hashCode()用於存儲和檢索值,因此在null鍵的情況下它是如何工作的?
null鍵是在HashMap中專門處理的,putForNullKey(V值)和getForNullKey()有兩種不同的方法。空鍵始終映射到索引0.為了在兩個最常用的操作(get和put)中執行,這個空key被拆分為單獨的方法,但在其他操作中包含條件。簡而言之,在HashMap中使用null鍵時,不使用equals()和hashcode()方法。
如圖:
3.4 JDK 1.7和JDK 1.8中的HashMap改進
JDK 1.7: 對HashMap和ArrayList進行了一些性能改進,減少內存消耗。當你創建HashMap例如新的HashMap()時,它會自動創建一個默認長度的數組
JDK 1.8:HashMap引入了一種改進的策略來處理碰撞。由於散列函數(例如總是返回相同存儲桶的位置),可以將HashMap轉換為連結列表,即將get()方法轉換為在O(n)而不是O(1)中執行。