java的HashMap的幾個問題
HashMap處理hash沖突的幾種方法
一、 開放定址法
Hi=(H(key) + di) MOD m i=1,2,...k(k<=m-1)其中H(key)為哈希函數;m為哈希表表長;di為增量序列。
開放定址法根據步長不同可以分為3種:
1)線性探查法(Linear Probing):di=1,2,3,...,m-1
簡單地說就是以當前沖突位置為起點,步長為1循環查找,直到找到一個空的位置就把元素插進去,循環完了都找不到說明容器滿了。就像你去一條街上的店裏吃飯,問了第一家被告知滿座,然後挨著一家家去問是否有位置一樣。
2)線性補償探測法:di=Q 下一個位置滿足 Hi=(H(key) + Q) mod m i=1,2,...k(k<=m-1) ,要求 Q 與 m 是互質的,以便能探測到哈希表中的所有單元。
3)偽隨機探測再散列:di=偽隨機數序列。還是那個例子,這是完全根據心情去選一家店來問了
缺點:
- 這種方法建立起來的hash表當沖突多的時候數據容易堆聚在一起,這時候對查找不友好;
- 刪除結點不能簡單地將被刪結 點的空間置為空,否則將截斷在它之後填人散列表的同義詞結點的查找路徑。因此在 用開放地址法處理沖突的散列表上執行刪除操作,只能在被刪結點上做刪除標記,而不能真正刪除結點
- 當空間滿了,還要建立一個溢出表來存多出來的元素。
二、再哈希法
Hi = RHi(key),i=1,2,...k
缺點:增加了計算時間。
三、建立一個公共溢出區
假設哈希函數的值域為[0,m-1],則設向量HashTable[0...m-1]為基本表,每個分量存放一個記錄,另設立向量OverTable[0....v]為溢出表。所有關鍵字和基本表中關鍵字為同義詞的記錄,不管他們由哈希函數得到的哈希地址是什麽,一旦發生沖突,都填入溢出表。
簡單地說就是搞個新表存沖突的元素。
四、鏈地址法(拉鏈法)
將所有關鍵字為同義詞的記錄存儲在同一線性鏈表中,也就是把沖突位置的元素構造成鏈表。
拉鏈法的優點:
- 拉鏈法處理沖突簡單,且無堆積現象,即非同義詞決不會發生沖突,因此平均查找長度較短;
- 由於拉鏈法中各鏈表上的結點空間是動態申請的,故它更適合於造表前無法確定表長的情況;
- 在用拉鏈法構造的散列表中,刪除結點的操作易於實現。只要簡單地刪去鏈表上相應的結點即可。
拉鏈法的缺點:
- 指針需要額外的空間,故當結點規模較小時,開放定址法較為節省空間,而若將節省的指針空間用來擴大散列表的規模,可使裝填因子變小,這又減少了開放定址法中的沖突,從而提高平均查找速度
HashMap在java1.8中為什麽用紅黑樹?
在jdk1.8版本後,java對HashMap做了改進,在鏈表長度大於8的時候,將後面的數據存在紅黑樹中,以加快檢索速度。
舉例:HashMap的put操作:
HashMap中數組的長度為什麽是2的n次冪?
在HashMap中采用indexFor方法返回保存該對象的位置信息:
static int indexFor(int h,int length){ return h & (length - 1); } static int hash(int h){ h ^= (h >>> 20) ^ (h >>>12); return h ^ (h >>> 7) ^ (h >>> 4); }
所以數組長度是2的n次方:
- h & (table.length - 1)的到對象的保存位,2的n次冪減去1在每個位數的值都是1,全部為1進行與操作速度會大大提高;
- 數組長度為2的n次慕時候,不同的key值算得的index相同概率較小,減小了hash碰撞的幾率,同樣查詢也不需要總是遍歷鏈表,查詢的效率得到了提高;
參考:
鏈接:https://www.jianshu.com/p/dff8f4641814
鏈接:https://blog.csdn.net/wushiwude/article/details/75331926
java的HashMap的幾個問題