I have been working with Java's HashMap lately and have run afoul of some interesting behavior. I am currently using it to store key / value objects with multiple fields. To do this I have overridden hashCode() and equals() as follows:
public final class TransitionState {
private String mStackSymbol;
private String mTransitionSymbol;
private int mState;
private static final int HASH_SEED = 7; //Should be prime
private static final int HASH_OFFSET = 31;
//Constructor and getter methods here
public boolean equals(TransitionState other) {
//Check that we aren't comparing against ourself
if (this == other) {
return true;
}
//Check that we are not comparing against null
if (other == null) {
return false;
}
//Check fields match
if ((mState == other.getState()) &&
(mTransitionSymbol.equals(other.getTransitionSymbol())) &&
(mStackSymbol.equals(other.getStackSymbol()))) {
return true;
} else {
return false;
}
}
public int hashCode() {
int intHash = HASH_SEED;
//Sum hash codes for individual fields for final hash code
intHash = (intHash * HASH_OFFSET) + mState;
intHash = (intHash * HASH_OFFSET) + (mTransitionSymbol == null ? 0 : mTransitionSymbol.hashCode());
intHash = (intHash * HASH_OFFSET) + (mStackSymbol == null ? 0 : mStackSymbol.hashCode());
return intHash;
}
}
Now, I am able to put items in the Map without issue. Retrieving them, however, is another story. Whenever I attempt to get() from the HashMap, NULL is returned. I wrote some test code to iterate over the Map and print values, which is where things get confusing as the hashCode() of my key objects matches that which I have in my map and equality with a known value returns true. Sample output as follows (see fourth transition from bottom of table):
Transition Table:
State Symbol Stack Move
--------------------------
1, a, b, (1, pop) with key hashcode 212603 and value hashcode 117943
0, b, a, (0, pop) with key hashcode 211672 and value hashcode 117912
1, b, z, (1, push) with key hashcode 212658 and value hashcode 3459456
0, a, b, (0, pop) with key hashcode 211642 and value hashcode 117912
1, a, z, (0, push) with key hashcode 212627 and value hashcode 3459425
0, a, a, (0, push) with key hashcode 211641 and value hashcode 3459425
0, a, z, (0, push) with key hashcode 211666 and value hashcode 3459425
0, b, z, (1, push) with key hashcode 211697 and value hashcode 3459456
1, b, a, (1, pop) with key hashcode 212633 and value hashcode 117943
1, b, b, (1, push) with key hashcode 212634 and value hashcode 3459456
ababba
Transition from (0, a, z) with hashcode 211666
transition.equals(new TransitionState(0, "a", "z")) = true
HashMap containsKey() = false
Transition not found
false
As you can see, the key matches hashcodes with an entry in the map, but I am being told that is does not exist. I tried debugging into the HashMap's containsKey() method which does a get() which is checked for NULL. Stepping into the get() shows the the loop there is only being run once before returning NULL.
So, is this a HashMap problem (likely not) or (more likely) what could I be doing wrong? Thank you in advance for your help.
You haven't overridden equals
properly... you need
public boolean equals(Object other)
Currently you're just overloading it.
Basically the real override could still do the same job as the existing one, but with a test first:
if (!(other instanceof TransitionState))
{
return false;
}
TransitionState otherState = (TransitionState) other;
// Now do the rest of the comparison
Note that you don't need the check for null in this case, as it would fail the instanceof
test.
These days Java allows you to add an annotation to tell the compiler you're really trying to override a parent method:
@Overrides
public boolean equals(Object other)
Now the compiler will tell you if you make a typo in the name or get the signature wrong.
Jon is correct. Additionally you should do the following:
Make your instance variables final:
private final String mStackSymbol;
private final String mTransitionSymbol;
private final int mState;
If you cannot do that then make sure you are not changing the values of those variables after you put the items into the map. If the state changes after you put them into the map you won't be able to get them out again (at least not by their original values).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With