作者:豹女无爱 | 来源:互联网 | 2023-01-19 19:57
我们假设我们有一些代码
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 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());
}
}
所以,我的问题是为什么在调整hashMap的大小之后(据我所知,直到重新调整键),我们仍然在keySet中有2个键而不是1个(因为现有的KV对的键对象是相同的)?
1> Gray..:
所以,我的问题是为什么在调整hashMap之后(到目前为止,据我所知,涉及重新调整键)
它实际上并没有,至少没有在-包括老调重弹键HashMap
,除了在某些情况下(见下文)代码.它涉及在地图桶中重新定位它们.里面HashMap
是一个Entry
具有以下字段的类:
final K key;
V value;
Entry next;
int hash;
该hash
字段是存储的密钥,用于在进行put(...)
调用时计算的密钥.这意味着如果更改对象中的哈希码,它将不会影响HashMap中的条目,除非您将其重新放入地图中.当然,如果您更改密钥的哈希码,您甚至无法在其中找到它,HashMap
因为它具有与存储的哈希条目不同的哈希码.
我们仍然在keySet中有2个键而不是1个(因为现有的KV对的密钥对象是相同的)?
因此,即使您已更改单个对象的哈希值,它也会在地图中包含2个条目,其中包含不同的哈希字段.
总而言之,当调整HashMap的大小时,HashMap
其中的代码可能会重新调整密钥 - 请参阅HashMap.transfer(...)
jdk 7中的软件包保护方法(至少).这就是hash
上面的字段不是的原因final
.它仅在initHashSeedAsNeeded(...)
返回true 时才使用"替代散列".以下设置启用alt-hashing的条目数阈值:
-Djdk.map.althashing.threshold=1
有了这个设置在VM上,我实际上能够hashcode()
在调整大小时再次调用,但是我无法将第二个put(...)
视为覆盖.问题的部分原因是,该HashMap.hash(...)
方法是做与内部的XOR hashseed
时调整大小会发生这变化,但之后的put(...)
记录进入进入新的哈希码.
这当然取决于`HashMap`的实现.我没有看过(例如)`LinkedHashMap`或`Map`的其他实现.没有什么能说`Map`实现不能多次调用`obj.hashcode()`,尽管如@jthlborn所说,这样做可能会很昂贵.
2> jtahlborn..:
HashMap实际上为每个键缓存 hashCode(因为键的hashCode计算起来可能很昂贵).因此,虽然您更改了现有键的hashCode,但它在HashMap中链接到的Entry仍然具有旧代码(因此在调整大小后将其置于"错误"桶中).
你可以在HashMap.resize()的jvm代码中看到这个(或者在java 6代码HashMap.transfer()中更容易看到).