hash系列的集合:
HashSet、LinkedHashSet 采用hash算法決定元素在集合中的存儲位置
HashMap、LinkedHashMap、Hashtable 采用hash算法決定key在集合中的存儲位置
hash表中可以存儲元素的位置,被稱為bucket(桶)。
在通常情況下,一個bucket里只存儲一個元素,此時性能最好,可根據hashCode直接定位元素所在的bucket,獲得元素。
但hash表的狀態是open的,在發生hash沖突時,一個bucket中會存儲多個元素,這些hash沖突的元素以鏈表形式存儲在一個bucket中:
此時hash表性能會下降,根據hash算法確定bucket位置后,還要遍歷鏈表,找到指定的元素。
如果我們重寫了自定義類的hashCode()、equals()則不會出現hash沖突的情況,一個bucket里只會存儲一個元素。
hash系列的集合都有以下屬性:
- capacity 容量,hash表中bucket的數量
- initial capacity 初始容量,創建hash表時bucket的數量
- size hash表中已裝元素的bucket數量
- load factor 負載因子,等於size/capacity,即已裝元素的bucket數占總bucket數的比例。0表示空的hash表,0.5表示半滿的hash表。
- 負載極限 0~1之間的一個float,表示當前hash表的最大填滿程度,即允許的load factor的最大值。
創建hash表時,此hash表的內存就確定了,根據hash算法確定的是元素在此hash表中的位置。
往hash表中添加元素時, 會先找到hash表中空的bucket,根據hash算法確定用哪個空的bucket來存儲元素。
load factor較小時,添加元素時很容易找到空的bucket,hash沖突少(因為可用的空bucket很多),存儲性能較高;已裝元素的bucket少,很容易從中找到指定的元素,查找性能較高;但遍歷集合(hash表)時,要過濾掉大量的空bucket,很花時間,所以遍歷時比較慢。
當load factor達到設置的負載極限時,會發生rehashing(重哈希/再散列),hash表會自動成倍地增加容量(capacity),將原有的元素都移到新的hash表中(會重新分配存儲位置),而此時原有的元素是極多的,這會增加很大的開銷。
負載極限設置較高時,節省內存(空桶較少),但添加、查找元素效率較低,時間開銷會增大;負載極限較低時,添加、查找元素效率較高,但會增加內存開銷。默認為0.75,是時間、空間的折中,我們可根據需要自行設置。
如果我們一開始就知道要存儲的元素個數,可以在創建hash表時就指定容量:元素總數/負載極限。這樣避免了rehashing,節省了時間開銷。且前中期hash表負載會很低,添加、查詢效率極高。
hash系列集合都有的3個重載構造函數:
() //無形參,使用默認的capacity、負載極限(0.75)
(int capacity) //指定容量
(int capacity,float 負載極限)