我正在分析一些旧的Java代码,看来我使用静态HashMap
和访问方法缓存值不起作用。
缓存代码(有点抽象):
static HashMap<Key, Value> cache = new HashMap<Key, Value>();
public static Value getValue(Key key){
System.out.println("cache size="+ cache.size());
if (cache.containsKey(key)) {
System.out.println("cache hit");
return cache.get(key);
} else {
System.out.println("no cache hit");
Value value = calcValue();
cache.put(key, value);
return value;
}
}
分析代码:
for (int i = 0; i < 100; i++)
{
getValue(new Key());
}
结果输出:
cache size=0
no cache hit
(..)
cache size=99
no cache hit
它看起来像Key
的哈希代码或等于代码中的标准错误。
但是:
new Key().hashcode == new Key().hashcode // TRUE
new Key().equals(new Key()) // TRUE
特别奇怪的是cache.put(key, value)
只是为hashmap添加了另一个值,而不是替换当前的值。
所以,我真的不知道这里发生了什么。我做错了吗?
修改的
好的,我在实际代码中看到Key
在其他方法和更改中被使用,因此它们会反映在hashCode
中对象的HashMap
中。这可能是这种行为的原因,它会丢失吗?
答案 0 :(得分:4)
@Override
equals/hashCode
@Override
我不相信你hashCode/equals
(你正在使用注释,对吧?)@Override
。如果您未使用int hashcode()
,则可能已定义boolean equals(Key)
或equals
,但这些都不会执行所需的操作。
如果你要改变地图的键,那么肯定会出现麻烦。来自the documentation:
注意:如果将可变对象用作映射键,则必须非常小心。如果在对象是地图中的关键字时,以影响
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"
比较的方式更改对象的值,则不会指定地图的行为。
以下是一个例子:
cache
顺便说一下,在类型声明中更喜欢接口到实现类。以下是 Effective Java 2nd Edition的引用:第52项:通过接口引用对象
[...]你应该倾向于使用接口而不是类来引用对象。 如果存在适当的接口类型,则应使用接口类型声明参数,返回值,变量和字段。
在这种情况下,如果可能的话,您应该将Map
简称为HashMap
而不是{{1}}。
答案 1 :(得分:0)
我建议对equals和hashCode方法进行双重和三重检查。请注意,它是hashCode,而不是hashcode。
答案 2 :(得分:0)
查看(抽象的)代码,一切似乎都是有序的。可能是实际代码与编辑版本不同,这更多地反映了您对代码的工作方式,而不是实践中发生的情况!
如果您可以发布代码,请执行此操作。与此同时,这里有一些尝试:
答案 3 :(得分:0)
我不确定你的Key
课程是什么,但是(抽象地类似于你)我做的简单检查是:
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);
如果此代码显示异常 - 相同的哈希码,相同的密钥,但尚未缓存 - 那么会引起担心(或者更好的是,调试您的{ {1}}类;-)。您使用新的Key
进行测试的方式可能会生成不一定表现相同的键。