3 ms·
> I can't remember ever having seen anyone use a mutable object for a hashmap key. HashMap keys aren't the only issue, sadly. In my experience, this is way mor
by elric 5y ago
> I can't remember ever having seen anyone use a mutable object for a hashmap key.
HashMap keys aren't the only issue, sadly. In my experience, this is way more common in HashSets when trying reduce a collection to a distinct set of items. If any of those items is shared and modified in another thread, you're in for a wonderful time debugging.
Mutability makes things so much harder to reason about. It'd be great if there were a language-assisted way to enforce immutability for collection members.
- rzzzt 5y agoHashSets are using a HashMap behind the scenes, they put a "present" placeholder value in the map when adding an element to the set: https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/share/classes/java/util/HashSet.java#L204-L218 https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89...
- elric 5y agoI'm not sure I understand your point. Yes, HashSet uses HashMap internally. But that's an implementation detail. I could have a Set<Foo> without knowing what the implementation is. If it's a TreeSet, things will work fine (as those use Comparators internally, and those don't usually rely on hashcode). If it's a HashMap, you can be boned without ever knowing why. IMO people are aware of the "mutable hashcode" issue when using maps, because you have to think about what the key of a map is when you're defining it, which is a good opportunity to remember that Foo is mutable. When you're using a Set, there's no key, only a value, which can sometimes be used as a key as an implementation detail, but there's no mental trigger.
- rzzzt 5y agoYou mentioned HashSets as another, distinct source of problems; I wanted to highlight that these two are the same thing behind the scenes, as the backing map's key set is doing the heavy lifting. It definitely is an implementation detail, and I can't say that all JDK variants employ the same trick, but a fair few of them do, given that most of them share the same set of java.lang classes.
- rzzzt 5y agoJust an addendum, sorry for the double post: I checked two additional implementations in the meantime. Apache Harmony: https://github.com/apache/harmony-classlib/blob/ee663b95e84093405985d9557a925b651a0f69b3/modules/luni/src/main/java/java/util/HashSet.java#L85-L96 https://github.com/apache/harmony-classlib/blob/ee663b95e840... GNU Classpath: https://github.com/ingelabs/classpath/blob/a96698f67357fddbe2026409d0113a35504bf6ab/java/util/HashSet.java#L142-L152 https://github.com/ingelabs/classpath/blob/a96698f67357fddbe...
- xxs 5y agoHashSet is just a HashMap (has a field) and that's pretty much it, it's quite inefficient (both perf and memory footprint) with HashMap bit inefficient to begin with.