Deterministic code is much easier to debug & understand than non-determinstic code. Got a test that’s failing? Launch the debugger, step through the code, and find the exact statement where an assumption is violated. For me this often looks like a binary search. I add breakpoints and make assertions like “everything is fine before we call pruneCache()”. I’ll run the app, learn something, and repeat.
When you use HashMap
in your programs, you’re introducing needless nondeterminism. This is because HashMap
’s iteration order is different between platforms (Android vs. Java) and between executions for classes that don’t override equals()
and hashCode()
.
This nondeterminism is toxic to debugging. I think it’s even worse than the nondeterminism caused by multithreading because it’s so unexpected.
Does this program always return the same result?
private static float massPerUnit(
float totalMass, float w, float h, float d) {
Map<CharSequence, Float> dimensions = new HashMap<>();
dimensions.put(dimension("width", "x"), w);
dimensions.put(dimension("height", "y"), h);
dimensions.put(dimension("depth", "z"), d);
float massPerUnit = totalMass;
for (Float dimension : dimensions.values()) {
massPerUnit /= dimension;
}
return massPerUnit;
}
Surprise, it doesn’t. And HashMap
’s non-deterministic iteration order is to blame.
LinkedHashMap
is great. It’s just as easy-to-use as HashMap
, and the runtime cost is negligible. Most of the performance & memory cost of HashMap
is in allocating and managing its hash entries, and LinkedHashMap
uses the same entries, just with extra next
and previous
links.
If you’re writing Java code, always prefer LinkedHashMap
over HashMap
. Even if you aren’t iterating the collection, it’s one less thing that can go wrong.