問題描述
假設(shè)我們有一些代碼
class WrongHashCode{
public int code=0;
@Override
public int hashCode(){
return code;
}
}
public class Rehashing {
public static void main(String[] args) {
//Initial capacity is 2 and load factor 75%
HashMap<WrongHashCode,String> hashMap=new HashMap<>(2,0.75f);
WrongHashCode wrongHashCode=new WrongHashCode();
//put object to be lost
hashMap.put(wrongHashCode,"Test1");
//Change hashcode of same Key object
wrongHashCode.code++;
//Resizing hashMap involved 'cause load factor barrier
hashMap.put(wrongHashCode,"Test2");
//Always 2
System.out.println("Keys count " + hashMap.keySet().size());
}
}
所以,我的問題是為什么在調(diào)整 hashMap 的大小后(據(jù)我所知,這涉及 重新散列鍵),我們在 keySet 中仍然有 2 個鍵而不是 1 個(因為鍵對象對于兩個現(xiàn)有的 KV 對)?
So, my question is why after resizing hashMap (that, as far, as I understand involves rehashing keys), we still have 2 keys in keySet instead of 1 (since key object is same for both existing KV pairs) ?
推薦答案
所以,我的問題是為什么在調(diào)整 hashMap 的大小之后(據(jù)我所知,這涉及重新散列鍵)
So, my question is why after resizing hashMap (that, as far, as I understand involves rehashing keys)
它實際上不涉及重新散列鍵 - 至少在 HashMap
代碼中沒有,除非在某些情況下(見下文).它涉及在地圖桶中重新定位它們.HashMap
內(nèi)部是一個 Entry
類,它具有以下字段:
It actually does not involve rehashing keys – at least not in the HashMap
code except in certain circumstances (see below). It involves repositioning them in the map buckets. Inside of HashMap
is a Entry
class which has the following fields:
final K key;
V value;
Entry<K,V> next;
int hash;
hash
字段是在進(jìn)行 put(...)
調(diào)用時計算的鍵的存儲哈希碼.這意味著如果您更改對象中的哈希碼,它不會影響 HashMap 中的條目,除非您將其重新放入映射中.當(dāng)然,如果您更改鍵的哈希碼,您甚至無法在 HashMap
中找到它,因為它與存儲的哈希條目具有不同的哈希碼.
The hash
field is the stored hashcode for the key that is calculated when the put(...)
call is made. This means that if you change the hashcode in your object it will not affect the entry in the HashMap unless you re-put it into the map. Of course if you change the hashcode for a key you won't be even able to find it in the HashMap
because it has a different hashcode as the stored hash entry.
我們在 keySet 中仍然有 2 個鍵而不是 1 個(因為兩個現(xiàn)有 KV 對的鍵對象相同)?
we still have 2 keys in keySet instead of 1 (since key object is same for both existing KV pairs) ?
因此,即使您更改了單個對象的哈希值,它仍在映射中,其中包含 2 個條目,其中包含不同的哈希字段.
So even though you've changed the hash for the single object, it is in the map with 2 entries with different hash fields in it.
話雖如此,HashMap
中的代碼可能在調(diào)整 HashMap 大小時重新散列鍵 - 請參閱包 protected HashMap.transfer(...)
jdk 7 中的方法(至少).這就是為什么上面的 hash
字段不是 final
的原因.但是,它僅在 initHashSeedAsNeeded(...)
返回 true 以使用替代哈希"時使用.以下設(shè)置啟用 alt-hashing 的條目數(shù)閾值:
All that said, there is code inside of HashMap
which may rehash the keys when a HashMap is resized – see the package protected HashMap.transfer(...)
method in jdk 7 (at least). This is why the hash
field above is not final
. It is only used however when initHashSeedAsNeeded(...)
returns true to use "alternative hashing". The following sets the threshold of number of entries where the alt-hashing is enabled:
-Djdk.map.althashing.threshold=1
通過在 VM 上設(shè)置此設(shè)置,我實際上能夠在調(diào)整大小時再次調(diào)用 hashcode()
,但我無法獲得第二個 put(...)
被視為覆蓋.部分問題是 HashMap.hash(...)
方法正在與內(nèi)部 hashseed
進(jìn)行 XOR,當(dāng)調(diào)整大小時會更改,但是 在之后,put(...)
記錄傳入條目的新哈希碼.
With this set on the VM, I'm actually able to get the hashcode()
to be called again when the resizing happens but I'm not able to get the 2nd put(...)
to be seen as an overwrite. Part of the problem is that the HashMap.hash(...)
method is doing an XOR with the internal hashseed
which is changed when the resizing happens, but after the put(...)
records the new hash code for the incoming entry.
這篇關(guān)于Java HashMap 調(diào)整大小的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!