Java HashMap关键问题

Java HashMap关键问题,java,key,hashmap,Java,Key,Hashmap,我正在分析一些旧的java代码,似乎使用静态HashMap和访问方法对值进行缓存不起作用 缓存代码(有点抽象): 它看起来像是键的哈希代码或equals代码中的标准错误。 然而: 特别奇怪的是,cache.put(key,value)只是将另一个值添加到hashmap中,而不是替换当前值 所以,我真的不明白这里发生了什么。我做错什么了吗 编辑 好的,我看到在真正的代码中,键被用于其他方法和更改中,因此在HashMap中对象的hashCode中得到了反映。这可能是导致该行为丢失的原因吗?对equa

我正在分析一些旧的java代码,似乎使用静态
HashMap
和访问方法对值进行缓存不起作用

缓存代码(有点抽象):

它看起来像是
的哈希代码或equals代码中的标准错误。 然而:

特别奇怪的是,
cache.put(key,value)
只是将另一个值添加到hashmap中,而不是替换当前值

所以,我真的不明白这里发生了什么。我做错什么了吗

编辑 好的,我看到在真正的代码中,
被用于其他方法和更改中,因此在
HashMap
中对象的
hashCode
中得到了反映。这可能是导致该行为丢失的原因吗?

equals/hashCode
我不相信您是否正确地使用了
@Override
(您正在使用注释,对吗?
hashCode/equals
)。如果未使用
@Override
,则可能已定义了
int hashcode()
,或
boolean equals(Key)
,这两种方法都不能满足要求


关于关键突变 如果你正在改变地图的关键点,那么是的,麻烦就会接踵而至。发件人:

注意:如果将可变对象用作贴图键,则必须非常小心。如果对象是贴图中的关键点时,对象的值以影响
等于
比较的方式更改,则不会指定贴图的行为

下面是一个例子:

Map<List<Integer>,String> map =
    new HashMap<List<Integer>,String>();
List<Integer> theOneKey = new ArrayList<Integer>();
map.put(theOneKey, "theOneValue");

System.out.println(map.containsKey(theOneKey)); // prints "true"
theOneKey.add(42);
System.out.println(map.containsKey(theOneKey)); // prints "false"
地图=
新的HashMap();
列出onekey=newarraylist();
map.put(theOneKey,“theOneValue”);
System.out.println(map.containsKey(theOneKey));//打印“真实”
theOneKey.add(42);
System.out.println(map.containsKey(theOneKey));//打印“假”
顺便说一下,在类型声明中,我们更喜欢接口而不是实现类。这里引用了《有效Java第二版》:第52项:通过接口引用对象

[…]您应该倾向于使用接口而不是类来引用对象。如果存在适当的接口类型,则应使用接口类型声明参数、返回值、变量和字段


在这种情况下,如果可能,您应该将
缓存
声明为简单的
映射
,而不是
哈希映射

,我建议对equals和hashCode方法进行双重和三重检查。请注意,这是hashCode,而不是hashCode。

查看(抽象的)代码,一切似乎都井然有序。可能是实际的代码与您的修订版本不同,这更多地反映了您期望代码如何工作,而不是实际发生的事情

如果你能发布代码,请这样做。同时,这里有一些建议可以尝试:

  • 添加密钥后,再次使用完全相同的密钥实例,并验证它是否生成缓存命中
  • 在测试中,验证哈希代码是否相等,以及对象是否相等
  • Map实现真的是HashMap吗?一旦无法访问密钥,WeakHashMap将按照您描述的方式运行
我不确定您的
键是什么,但是(抽象地与您类似)我要做的一个简单检查是:

Key k1 = new Key();
Key k2 = new Key();
System.out.println("k1 hash:" + k1.hashcode);
System.out.println("k2 hash:" + k2.hashcode);
System.out.println("ks equal:" + k1.equals(k2));
getValue(k1);
getValue(k2);

如果此代码显示异常情况—相同的hashcode,相等的键,但还没有缓存--,那么就有理由担心了(或者,更好的是,调试
类;-)。您一直使用新的
s进行测试的方式可能会产生不一定具有相同行为的键。

我尝试过,它给出了完全相同的代码。然而,经过更多的调查,可能是我对我的案件做了过多的分析。在实际的代码中,在其他方法中缓存后对键进行更改,这些更改会反映在hashcode中,因此我认为它会丢失在hashmap中。@Peterdk:这肯定可以做到。更改映射中用作键的对象是一个非常糟糕的主意。是的,使用实现类是因为我在Android上,为了提高性能,建议不要使用接口。您不应该使用hashCode/equals/comapreTo中的任何字段,这些字段在添加到映射后都可以修改。这实际上破坏了地图。最安全的做法是将这些字段设置为最终字段,如果不能设置为最终字段,则必须质疑您的设计选择。虽然IMHO.WeakHashMap可以做到这一点,但它不太可能足够快地删除密钥,以跟上添加它们的循环。每次迭代都需要一个GC。此外,尺寸会减小。
 cache size=0
 no cache hit
 (..)
 cache size=99
 no cache hit
 new Key().hashcode == new Key().hashcode // TRUE
 new Key().equals(new Key()) // TRUE
Map<List<Integer>,String> map =
    new HashMap<List<Integer>,String>();
List<Integer> theOneKey = new ArrayList<Integer>();
map.put(theOneKey, "theOneValue");

System.out.println(map.containsKey(theOneKey)); // prints "true"
theOneKey.add(42);
System.out.println(map.containsKey(theOneKey)); // prints "false"
Key k1 = new Key();
Key k2 = new Key();
System.out.println("k1 hash:" + k1.hashcode);
System.out.println("k2 hash:" + k2.hashcode);
System.out.println("ks equal:" + k1.equals(k2));
getValue(k1);
getValue(k2);