I’m profiling some old java code and it appears that my caching of values using a static HashMap and a access method does not work.
Caching code (a bit abstracted):
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;
}
}
Profiling code:
for (int i = 0; i < 100; i++)
{
getValue(new Key());
}
Result output:
cache size=0
no cache hit
(..)
cache size=99
no cache hit
It looked like a standard error in Key‘s hashing code or equals code.
However:
new Key().hashcode == new Key().hashcode // TRUE
new Key().equals(new Key()) // TRUE
What’s especially weird is that cache.put(key, value) just adds another value to the hashmap, instead of replacing the current one.
So, I don’t really get what’s going on here. Am I doing something wrong?
edit
Ok, I see that in the real code the Key gets used in other methods and changes, which therefore get’s reflected in the hashCode of the object in the HashMap. Could that be the cause of this behaviour, that it goes missing?
On a proper
@Overrideofequals/hashCodeI’m not convinced that you
@Override(you are using the annotation, right?)hashCode/equalsproperly. If you didn’t use@Override, you may have definedint hashcode(), orboolean equals(Key), neither of which would do what is required.On key mutation
If you are mutating the keys of the map, then yes, trouble will ensue. From the documentation:
Here’s an example:
By the way, prefer interfaces to implementation classes in type declarations. Here’s a quote from Effective Java 2nd Edition: Item 52: Refer objects by their interfaces
In this case, if at all possible, you should declare
cacheas simply aMapinstead of aHashMap.