4 ms·
"It is a frequent source of bugs to change the object in such a way that its hashCode changes after it was used" (citation needed) I have never seen that. 20
by teknopaul 5y ago
"It is a frequent source of bugs to change the object in such a way that its hashCode changes after it was used"
(citation needed)
I have never seen that. 20 years in the game.
I can't remember ever having seen anyone use a mutable object for a hashmap key.
- xxs 5y ago>I have never seen that. 20 years in the game. I have seen that a bit too many times, it's pretty terrible. People debug and print HashMap contents, and complain the thing is broken... as get(Object) just returns null, finding an empty bucket. I guess, you have been a bit more lucky than I was. Definitely seen too many mutable keys.
- teknopaul 5y agoto be honest I can't bring to mimd any code that changes a value without recalling put(), code that does that would smell so bad you would spot it by the @author
- mumblemumble 5y agoIt's all private code, so I can't give you a citation, but I've often helped junior devs with bugs due to this. The most common way to get there starts with creating a "bean" object. And then, since it's a bean, it has mutable getters and setters. On everything, because that's just the standard way that people are (or at least were) taught to do these things in Java. And then you ask your IDE to give you equals and hashCode implementations, and yeah sure whatever just use all the fields. And then, later on, you need some sort of lookup table that cross-references the data modeled by this bean with some outside source of information. So of course that's a HashMap. And then you go through your objects and update them all based on that information. Maybe you just edit the description or the lastUsed field, who knows. All sorts of possibilities here. In any case, there's your bug.
- cogman10 5y agoI've seen it as well. Usually it takes the form of "I added object x to a HashSet, then while processing things I update a field in object x used in the hashcode. Object x no longer appears in the set".
- 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.
- firebaze 5y agoJust to add another anecdotal data point: me neither. Neither in java, nor in .Net, and surely not in C++ (there were lots of other things to mess up), having around ~15 years of experience under my belt. To me it is quite elementary, almost muscle memory, to never mutate objects used as keys - and while I observed quite a few rookie mistakes (some of them from the "I" perspective), it'd never occur to me to change a key in a map. I'd probably learn a lot from stories explaining the environment of such situations, so if you have one, please share.
- andersonvieira 5y agoI have seen it happen many times. A lot of developers I've worked with do not understand the basics of a HashMap/HashSet. They just use the IDE to automatically create hashCode functions with mutable attributes or use classes created by other people and do not check if the hashCode implementation is correct.
- usrusr 5y agoWe see that surprisingly rarely because the completely clueless tend to get saved by identity hashcode/equals. (and the lazy, and often even the pragmatic) Wether or not you get to see code falling into the mutable hash trap probably depends a lot on culture and the hiring process/environment. Do people feel empowered to do the right thing? Or free to take whatever easy path that works for them? Or are they pressured/incentivized to aim for some superficial appearance metrics?
- jhhh 5y agoMaybe you only work on your own code more often than not but I've had discussions during code reviews with junior developers on why this is bad practice.
- jillesvangurp 5y agoSame here. Mostly, the reason is that static code analysis tools are pretty good at spotting dodgy equals and hash code implementations. Also people have been using libraries and ide support to help them create good implementations or things like Lombok that generate good implementations. These days with Kotlin data classes or Java's records or value classes, there should rarely be a need to roll your own hashcode and equals implementations. Finally, mutability is a lot less popular these days. I feel kind of dirty every time I have to use an actual var in Kotlin. Having everything immutable by default makes mutability an opt in choice. Kotlin actually uses compiler plugins to deal with frameworks that need data classes to be mutable (like hibernate) just so you can pretend they are immutable while programming. So, accidental mutation is not a thing.