1. 程式人生 > >HashMap和HashSet總結

HashMap和HashSet總結

本文是作者在專案過程中做的總結,內容既有借鑑其他大神的地方,也有自己結合當前專案的思考。若有錯誤的地方,歡迎指正!最後感謝以下作者的分享!!

參考來源1:http://www.importnew.com/7099.html

參考來源2:https://blog.csdn.net/chenssy/article/details/18323767

參考來源3:https://blog.csdn.net/chenssy/article/details/21988605


1.HashMap

       它是基於雜湊表的Map介面的實現,是一種支援快速存取的資料結構,以鍵值對的形式儲存資料。在HashMap中,key-value會被當做一個整體來處理,系統會根據hash演算法來計算key-value的位置,實現通過key快速存、取value。

1.1定義

public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable

       HashMap繼承AbstractMap類(AbstractMap實現了Map介面),在這裡又再次實現了Map介面(當初寫這段程式碼的 Josh Bloch說這就是一個寫法錯誤。詳情見https://stackoverflow.com/questions/2165204/why-does-linkedhashsete-extend-hashsete-and-implement-sete

)。

       Map介面定義了鍵對映到值的規則,而AbstractMap類提供Map介面的骨幹實現,以最大限度的減少實現此介面所需的工作。

1.2影響HashMap效能的重要引數

1.2.1初始容量

       容量表示雜湊表中桶的數量,初始容量是建立雜湊表時的容量。若未指定容量,則預設初始容量為16。

1.2.2載入因子

       載入因子是雜湊表在容量自動增加之前可以達到多滿的一種尺度,它衡量的是一個散列表空間的使用程度。載入因子越大表示散列表的裝填程度越高,反之愈小。對於連結串列雜湊來說,查詢一個元素的平均時間是O(1+a),因此如果負載因子越大,對空間的利用更充分,然而後果是查詢效率的降低;如果負載因子太小,那麼散列表的資料將過於稀疏,對空間造成嚴重浪費。系統預設負載因子為0.75,一般情況下我們是無需修改的。

1.3資料結構

       每次新建一個HashMap,都會初始化一個table陣列。table陣列的元素為Entry節點。其中Entry為HashMap的內部類,它包含了鍵key,值value,下一個節點next以及hash值。正是由於Entry才構成了table陣列的項為連結串列。

1.4儲存實現:put(key,value)

       當我們向一個HashMap新增一對key-value時,首先根據key計算hash值,然後根據hash值確定在table陣列中的位置,即bucket的位置。若該位置沒有元素,則直接插入,否則迭代此處的entry節點構成的連結串列。若兩個hash值相等且key值相等(e.hash == hash && ((k = e.key) == key || key.equals(k))),則用新的entry的value覆蓋原來節點的value。如果兩個hash值相等但key值不等,則將該節點插入連結串列的鏈頭。

1.5讀取實現:get(key)

       讀取的前提是:hashcode相同,bucket位置相同。hashcode相同,key不一定相同;key相同,hashcode一定相同。

通過key的hash值找到table陣列中的索引處的連結串列,遍歷連結串列,找到hash和key值相等的Entry節點。

1.6相關面試題

1.6.1你知道HashMap的工作原理嗎?

       HashMap是基於hashing的原理,我們使用put(key,value)儲存物件到HashMap中,使用get(key)從HashMap中獲取物件。當我們給put方法傳遞鍵值時,我們先對鍵呼叫hashcode()方法,返回的hash值用於找到bucket位置來儲存Entry物件。

1.6.2當兩個物件的hashcode相同會發生什麼?

       因為hashcode相同,bucket位置相同,‘碰撞’會發生。遍歷bucket位置的連結串列,由Entry節點構成的連結串列,若hash值和key值相等,則用新的Entry節點的value替換原來節點的value,否則將新的Entry節點插入連結串列的表頭。

1.6.3如果兩個鍵的hashcode相同,你如何獲取值物件?

       通過hashcode找到bucke位置,遍歷連結串列,判斷key值是否相等,直到找到值物件。

1.6.4如果HashMap的大小超過了負載因子(load factor)定義的容量,怎麼辦?

       預設的負載因子大小為0.75,也就是說,當一個map填滿了75%的bucket時候,和其它集合類(如ArrayList等)一樣,將會建立原來HashMap大小的兩倍的bucket陣列,來重新調整map的大小,並將原來的物件放入新的bucket陣列中。這個過程叫作rehashing,因為它呼叫hash方法找到新的bucket位置。

1.7Java8新特性:Map computeIfAbsent方法說明

參考來源:https://blog.csdn.net/weixin_38229356/article/details/81129320

1.8什麼時候用HashMap?

1.當一個函式需要return多個引數時,可以通過Map返回。

2.當函式形參較多或者不確定個數時,可以通過Map傳參。

3.對大量資料進行分組整理,例如:本專案中的供應商資訊根據能提供的原料id進行分組。

4.實現下拉列表key-value可以通過List<Map>的方式實現。

5.字典值 通過value找name :Map<String,Map<String,String>>

例如:訂單型別(order_type):日(day)周(week)月(month) firstMap.put("order_type",secondMap)

secondMap.put("day","日"),secondMap.put("week","周"),secondMap.put("month","月")

6.將List/array轉為Map,將判斷依據放到key,要獲取的資訊放到value,利用Map快速存取的特性實現快速判斷集合中是否包含某個元素,從而提高程式的執行效率。

7.JSONArray->JSONObject->JSONString的轉換:將JSONArray放到map中,由於JSONObject本身就是一個map,再通過JSON.toJSONString()方法將JSON物件轉化為JSON字串。


2.HashSet

       HashSet是基於HashMap來實現的,底層採用HashMap來儲存元素。

2.1定義

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable

       Set介面是一種不包含重複元素的collection,它維持它自己的內部排序,所以隨機訪問沒有意義。簡而言之,HashSet是無序、不重複的集合。(HashMap是按照HashCode大小進行排序)

2.2基本屬性

//基於HashMap實現,底層使用HashMap儲存所有元素
private transient HashMap<E,Object> map;
 
 //定義一個Object物件作為HashMap的value
 private static final Object PRESENT = new Object();

       HashSet中的所有元素都是儲存在HashMap的key中,value則是使用的PRESENT物件。由於HashMap中key不會重複,因此滿足 了HashSet中元素不會重複的特性。

2.3什麼時候用HashSet?

       當集合元素需要去重且對元素的順序沒有要求時使用!例如:本專案中的獲取所有原料單位、商品單位、選單id。