Java HashMap get works but containsKey does not - java

I am trying to locate a key in a HashMap. I can print the selected key by using 'get' but when I use 'containsKey' in an if statement, it is not found.
I KNOW the key is present in the Map but it keeps returning false. Any ideas people?
My code:
public static boolean checkLowerStructuralSupport(Location location) {
boolean hasSupport = false;
Location supportingLocation = new Location(location.getX(), location.getY(), location.getZ() - 1);
System.out.println(_levels.get(supportingLocation.getZ()).getLevelSites2().get(supportingLocation)); //works
if (_levels.get(supportingLocation.getZ()).getLevelSites2().containsKey(supportingLocation)) {
hasSupport = true;
} else {
hasSupport = false;
}
return hasSupport;
}
Here is the code for the Location class:
public class Location {
protected int _x;
protected int _y;
protected int _z;
public Location(int xAxis, int yAxis, int zAxis) {
this._x = xAxis;
this._y = yAxis;
this._z = zAxis;
}
public void equals() {
//not implemented yet
}
public void HashCode() {
//not implemented yet
}
public String toString() {
String locationString = Integer.toString(_x) + Integer.toString(_y) + Integer.toString(_z);
return locationString;
}
public void setX(int XAxis) {
this._x = XAxis;
}
public int getX() {
return this._x;
}
public void setY(int YAxis) {
this._y = YAxis;
}
public int getY() {
return this._y;
}
public void setZ(int ZAxis) {
this._z = ZAxis;
}
public int getZ() {
return this._z;
}
}

You must ensure that the Location class has properly implemented its hashCode() and equals(Object) methods (documentation). That is, if two Location objects are effectively equal, they should share a common hash code and their equals method should return true.

As descibed here, you have to override the equals(Object) method.
The reason why get(Object) is working is, that HashMap will calculate the Hash for your Location class and returns the Object the hascode points to.
containsKey(Object) calculates the hash key and gets the object the hash is pointed to. The object from the HashMap will compare to the Object you put in. For these comparison the equals method is used.
When you do not override he equals method, true is returned, when the object reference to the same instance.
From HashMap
/**
* Check for equality of non-null reference x and possibly-null y.
*/
static boolean eq(Object x, Object y) {
return x == y || x.equals(y);
}
From Object
public boolean equals(Object obj) {
return (this == obj);
}
From the javadoc of equals
The equals method for class Object
implements the most discriminating
possible equivalence relation on
objects; that is, for any non-null
reference values x and y, this method
returns true if and only if x and y
refer to the same object (x == y has
the value true).
Note that it is generally necessary to
override the hashCode method whenever
this method is overridden, so as to
maintain the general contract for the
hashCode method, which states that
equal objects must have equal hash
codes.

In Location class, make sure you are overriding hashCode and equals methods.
If you are, can you post them?

containsKey uses the method equals to compare the param with the entries in the key set. So the Location class needs to have a equals method that is good. The default equals method in java.lang.Object only returns true when both objects are the same object. In this case you probably have 2 different instances that needs to be compared and need a custom equals method.

The only thing I can think of that will cause this is if the state of supportingLocation is somehow being mutated between the get(...) call and the containsKey(...).
Assuming the code snippet you posted is the exact code that's causing problems, the only place this could occur is if one of Location#getZ(...), Location#hashCode() or Location#equals(Object) mutates the state of Location (or the Location constructor, or one of these methods starts a thread that randomly changes the state of the Location instance, but I think we can rule that out).
Could you verify that none of the methods above are changing the state of the supportingLocation instance? While I am not familiar with the Location class itself, I'd venture to guess that a class like that would ideally be immutable.
Edit:
To clarify, when I say that Location#getZ() etc aren't mutating the Location, what I mean is:
Location x = new Location(1,2,3);
Location y = new Location(1,2,3);
boolean eq1 = x.equals(y);
int hash1 = x.hashCode();
x.getZ(); // this should *not* mutate the state of x
boolean eq2 = x.equals(y);
int hash2 = x.hashCode();
In the end, eq1 should be equal to eq1, and hash1 should be equal to hash2. If this is not the case, getZ() is mutating the state of x (or equals, or hashCode, or worse, those methods are completely off), and will result in the behavior you observed.

To avoid problems, your equals() and hashCode() methods should be consistent and conform to the requirements (as noted elsewhere).
Additionally, hashCode() should not rely on mutable members, otherwise your calculated hash code can change, and this affects the internal workings of the HashMap. That will reveal itself in an inability to retrieve stuff from Hash* collections.

Take a peak at the source code for the HashMap implementation. Both get and containsKey use the hasCode() and equals() methods of your key object.
The only real difference, and as was pointed out, it is a trivial null check, is in the comparisons:
get:
((k = e.key) == key || key.equals(k))
containsKey:
((k = e.key) == key || (key != null && key.equals(k)))
where e is of type Entry for a HashMap.
So, if you do not have a strong implementations of hashCode() and/or equals() you will have a problem. Additionally, if your keys were mutated (I see that you did not declare the class fields final) you could have an issue.
Take the following example:
public class HashMapTest {
static class KeyCheck {
int value;
public KeyCheck(int value) { this.value = value; }
public void setValue(int value) { this.value = value; }
#Override public int hashCode() { return value; }
#Override public boolean equals(Object o) {
return ((KeyCheck)o).value == this.value;
}
}
public static void main(String args[]) {
HashMap<KeyCheck, String> map = new HashMap<KeyCheck, String>();
KeyCheck k1 = new KeyCheck(5);
KeyCheck k2 = new KeyCheck(5);
map.put(k1, "Success");
System.out.println("Key: " + k1 + " Get: " + map.get(k1) +
" Contains: " + map.containsKey(k1));
System.out.println("Key: " + k2 + " Get: " + map.get(k2) +
" Contains: " + map.containsKey(k2));
k1.setValue(10);
System.out.println("Key: " + k1 + " Get: " + map.get(k1) +
" Contains: " + map.containsKey(k1));
System.out.println("Key: " + k2 + " Get: " + map.get(k2) +
" Contains: " + map.containsKey(k2));
}
}
This will print out:
Key: HashMapTest$KeyCheck#5 Get: Success Contains: true
Key: HashMapTest$KeyCheck#5 Get: Success Contains: true
Key: HashMapTest$KeyCheck#a Get: null Contains: false
Key: HashMapTest$KeyCheck#5 Get: null Contains: false
As you can see, in this case the mutability caused the hashCode() to change, which ruined everything.

Both get() and containsKey() are using the Location class's hashCode() method. The equals() method isn't called unless there is a hash collision. (thus, HashMap's get() won't use equals() in every situation.)
For your Location class, did you by chance happen to implement your own version of hashCode()? The hashCode() method should be implemented carefully. Joshua Bloch described all the details in the book Effective Java, portions of which are online... I'll go find the link to those sample chapters: Effective Java Sample Chapters. You want chapter 3.
As I asked in the comment to the question, Where does your _levels variable come from? I don't see it declared inside that method and your naming (underscore prefix, are you importing that convention from some other language?) suggests that it "lives" outside this method. Perhaps other code is changing it during execution? Please let us know when you solve it; the suspense is killing me.

i think sometime you need the hash code and sometimes not so i think in this way you can turn of the hash code checking when you want buy changing the hash code for all objects you want to 0
public class sample(){
#JsonIgnore
private int hashCode = super.hashCode();
public void setHashCode(int hashCode){
this.hashCode = hashCode;
}
#Override
public int hashCode(){
return this.hashCode;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ReflectObject other = (ReflectObject) obj;
if (this.hashCode != other.hashCode) {
return false;
}
return true;
}
}

Related

Override equals method to return false while inserting to HashMap

import java.util.HashMap;
import java.util.Map;
public class MapTest {
public String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Override
public int hashCode(){
return 100;
}
#Override
public boolean equals(Object te){
return false;
}
public static void main(String[] args) {
MapTest obj = new MapTest();
MapTest obj1 = new MapTest();
obj.setId("test");
obj1.setId("test2");
Map<MapTest,Integer> test = new HashMap<MapTest,Integer>();
test.put(obj, 1000);
test.put(obj1, 2000);
test.put(new MapTest(), 4000);
System.out.println(test.get(obj)); //1000
System.out.println(test.get(obj1));//2000
System.out.println(test.get(new MapTest()));//Null
System.out.println(test.size());//3
}
}
Here the object returns same hashcodes so inorder to avoid overwriting while adding data to HashMap I have a equals method that returns false. Now when I use get I get the proper data example obj1 and obj are giving exact values but my question is when both their hashcodes are same how java distinguishes different objects and return their exact values from hashmap.
The answer is in how HashMap implements the get method. When it checks the equality of the given key against the keys stored in the map (when they have equal hashcode value), it first does a reference equality check. The code below is taken from Java 8's implementation of get and as you see, there is the == check for the keys.
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
If you use another object, with the same id value, it will not work, as there is no structural equality defined in your objects. Try this:
TestMap obj2 = new TestMap();
obj2.setId("test");
System.out.println(test.get(obj2)); // Returns Null
The hashcode equals is the first requirement for hashmap to decide whether two keys the same, but not the only one/
(key1 == key2 || key1.equals(key2)) is also needed.
If you change your equals method to always return true, you can see there will be only one element in the map at last.

Why is my HashMap.containsKey(myKey) always returning false? [duplicate]

I am trying to locate a key in a HashMap. I can print the selected key by using 'get' but when I use 'containsKey' in an if statement, it is not found.
I KNOW the key is present in the Map but it keeps returning false. Any ideas people?
My code:
public static boolean checkLowerStructuralSupport(Location location) {
boolean hasSupport = false;
Location supportingLocation = new Location(location.getX(), location.getY(), location.getZ() - 1);
System.out.println(_levels.get(supportingLocation.getZ()).getLevelSites2().get(supportingLocation)); //works
if (_levels.get(supportingLocation.getZ()).getLevelSites2().containsKey(supportingLocation)) {
hasSupport = true;
} else {
hasSupport = false;
}
return hasSupport;
}
Here is the code for the Location class:
public class Location {
protected int _x;
protected int _y;
protected int _z;
public Location(int xAxis, int yAxis, int zAxis) {
this._x = xAxis;
this._y = yAxis;
this._z = zAxis;
}
public void equals() {
//not implemented yet
}
public void HashCode() {
//not implemented yet
}
public String toString() {
String locationString = Integer.toString(_x) + Integer.toString(_y) + Integer.toString(_z);
return locationString;
}
public void setX(int XAxis) {
this._x = XAxis;
}
public int getX() {
return this._x;
}
public void setY(int YAxis) {
this._y = YAxis;
}
public int getY() {
return this._y;
}
public void setZ(int ZAxis) {
this._z = ZAxis;
}
public int getZ() {
return this._z;
}
}
You must ensure that the Location class has properly implemented its hashCode() and equals(Object) methods (documentation). That is, if two Location objects are effectively equal, they should share a common hash code and their equals method should return true.
As descibed here, you have to override the equals(Object) method.
The reason why get(Object) is working is, that HashMap will calculate the Hash for your Location class and returns the Object the hascode points to.
containsKey(Object) calculates the hash key and gets the object the hash is pointed to. The object from the HashMap will compare to the Object you put in. For these comparison the equals method is used.
When you do not override he equals method, true is returned, when the object reference to the same instance.
From HashMap
/**
* Check for equality of non-null reference x and possibly-null y.
*/
static boolean eq(Object x, Object y) {
return x == y || x.equals(y);
}
From Object
public boolean equals(Object obj) {
return (this == obj);
}
From the javadoc of equals
The equals method for class Object
implements the most discriminating
possible equivalence relation on
objects; that is, for any non-null
reference values x and y, this method
returns true if and only if x and y
refer to the same object (x == y has
the value true).
Note that it is generally necessary to
override the hashCode method whenever
this method is overridden, so as to
maintain the general contract for the
hashCode method, which states that
equal objects must have equal hash
codes.
In Location class, make sure you are overriding hashCode and equals methods.
If you are, can you post them?
containsKey uses the method equals to compare the param with the entries in the key set. So the Location class needs to have a equals method that is good. The default equals method in java.lang.Object only returns true when both objects are the same object. In this case you probably have 2 different instances that needs to be compared and need a custom equals method.
The only thing I can think of that will cause this is if the state of supportingLocation is somehow being mutated between the get(...) call and the containsKey(...).
Assuming the code snippet you posted is the exact code that's causing problems, the only place this could occur is if one of Location#getZ(...), Location#hashCode() or Location#equals(Object) mutates the state of Location (or the Location constructor, or one of these methods starts a thread that randomly changes the state of the Location instance, but I think we can rule that out).
Could you verify that none of the methods above are changing the state of the supportingLocation instance? While I am not familiar with the Location class itself, I'd venture to guess that a class like that would ideally be immutable.
Edit:
To clarify, when I say that Location#getZ() etc aren't mutating the Location, what I mean is:
Location x = new Location(1,2,3);
Location y = new Location(1,2,3);
boolean eq1 = x.equals(y);
int hash1 = x.hashCode();
x.getZ(); // this should *not* mutate the state of x
boolean eq2 = x.equals(y);
int hash2 = x.hashCode();
In the end, eq1 should be equal to eq1, and hash1 should be equal to hash2. If this is not the case, getZ() is mutating the state of x (or equals, or hashCode, or worse, those methods are completely off), and will result in the behavior you observed.
To avoid problems, your equals() and hashCode() methods should be consistent and conform to the requirements (as noted elsewhere).
Additionally, hashCode() should not rely on mutable members, otherwise your calculated hash code can change, and this affects the internal workings of the HashMap. That will reveal itself in an inability to retrieve stuff from Hash* collections.
Take a peak at the source code for the HashMap implementation. Both get and containsKey use the hasCode() and equals() methods of your key object.
The only real difference, and as was pointed out, it is a trivial null check, is in the comparisons:
get:
((k = e.key) == key || key.equals(k))
containsKey:
((k = e.key) == key || (key != null && key.equals(k)))
where e is of type Entry for a HashMap.
So, if you do not have a strong implementations of hashCode() and/or equals() you will have a problem. Additionally, if your keys were mutated (I see that you did not declare the class fields final) you could have an issue.
Take the following example:
public class HashMapTest {
static class KeyCheck {
int value;
public KeyCheck(int value) { this.value = value; }
public void setValue(int value) { this.value = value; }
#Override public int hashCode() { return value; }
#Override public boolean equals(Object o) {
return ((KeyCheck)o).value == this.value;
}
}
public static void main(String args[]) {
HashMap<KeyCheck, String> map = new HashMap<KeyCheck, String>();
KeyCheck k1 = new KeyCheck(5);
KeyCheck k2 = new KeyCheck(5);
map.put(k1, "Success");
System.out.println("Key: " + k1 + " Get: " + map.get(k1) +
" Contains: " + map.containsKey(k1));
System.out.println("Key: " + k2 + " Get: " + map.get(k2) +
" Contains: " + map.containsKey(k2));
k1.setValue(10);
System.out.println("Key: " + k1 + " Get: " + map.get(k1) +
" Contains: " + map.containsKey(k1));
System.out.println("Key: " + k2 + " Get: " + map.get(k2) +
" Contains: " + map.containsKey(k2));
}
}
This will print out:
Key: HashMapTest$KeyCheck#5 Get: Success Contains: true
Key: HashMapTest$KeyCheck#5 Get: Success Contains: true
Key: HashMapTest$KeyCheck#a Get: null Contains: false
Key: HashMapTest$KeyCheck#5 Get: null Contains: false
As you can see, in this case the mutability caused the hashCode() to change, which ruined everything.
Both get() and containsKey() are using the Location class's hashCode() method. The equals() method isn't called unless there is a hash collision. (thus, HashMap's get() won't use equals() in every situation.)
For your Location class, did you by chance happen to implement your own version of hashCode()? The hashCode() method should be implemented carefully. Joshua Bloch described all the details in the book Effective Java, portions of which are online... I'll go find the link to those sample chapters: Effective Java Sample Chapters. You want chapter 3.
As I asked in the comment to the question, Where does your _levels variable come from? I don't see it declared inside that method and your naming (underscore prefix, are you importing that convention from some other language?) suggests that it "lives" outside this method. Perhaps other code is changing it during execution? Please let us know when you solve it; the suspense is killing me.
i think sometime you need the hash code and sometimes not so i think in this way you can turn of the hash code checking when you want buy changing the hash code for all objects you want to 0
public class sample(){
#JsonIgnore
private int hashCode = super.hashCode();
public void setHashCode(int hashCode){
this.hashCode = hashCode;
}
#Override
public int hashCode(){
return this.hashCode;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ReflectObject other = (ReflectObject) obj;
if (this.hashCode != other.hashCode) {
return false;
}
return true;
}
}

Is it possible to enter duplicate value in HashSet?

I am trying to add duplicate values in HashSet by modifying its hashCode() and equals() method()?
I tried below code
public class dupSet {
static Set set= new HashSet();
#Override
public int hashCode() {
return (int) (100*Math.random());
}
#Override
public boolean equals(Object obj) {
return false;
}
public static void main(String[] args) throws ParseException {
set.add("a");
set.add("b");
set.add("a");
System.out.println(set);
}
}
As per my understanding if for two duplicate of "a" HashSet will first get hashCode() to get proper bucket and then check value of equals() if equals returns true then it will not add but if it return false then it will add.
So for adding duplicate value to my Set I override equals() which always return false but still set is not allowing duplicate values?
You hashCode method returns always zero. Have a look at the range of Math.random().
Second, you do not override equals and hashCode of the elements you add. You actually add a String. To make things work, you must implement a class and add instances of that class to you HashSet. The implemented class needs to override the equals and hashSet method, not the main class.
Third, as stated in the comments, you shouldn't do what you are doing. What you realy want is a ArrayList. By implementing the equals and hashCode methods this way, a fundamental contract is broken.
I read source code and from that I am able to understand how its work
so need some help
First of all
Set is a collection of well defined and distinct objects
So there is no question of adding duplicates values. But if you are interested in understanding how java achieve/implement this constraint , then you can start digging in the source code.
A HashSet is backed by HashMap which mean that it delegates it operations like add, remove, etc. to HashMap .Now When you call set.add("a"); then
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
is called, which in turn calls HashMap#put
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
The put method first calcuates the hash code of the object using
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
Once the hashCode is calculated the it calls
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)
inside this method , it put the value because this condition
if ((p = tab[i = (n - 1) & hash]) == null)
is true and it then increments the modCount(which stores the number of times the HashMap has been structurally modified), checks if we need to resize the map and then call afterNodeInsertion and returns null
Now when you call set.add("b"); then the same logic runs again but this time the condition inside final V putVal method
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
holds true and due to this , the code
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
detects the existing mapping and thus return the oldValue . Hence preventing adding duplicate value.
You want the objects in the Set to include duplicates I assume (if just for curiosity keep reading, otherwise just choose other collection. this might help)
Let me make some corrections:
public class DupSet<E extends Comparable<E>>{
private Set<E> mySet = new HashSet<>();
//Implement add, remove and size
}
public class MyNeverEqualClass implements Comparable<MyNeverEqualClass>{
private static int stupidHash = 0;
private int num;
public MyNeverEqualClass(int num){
this.num = num;
}
#Override
public int compareTo(MyNeverEqualClass other){
double rnd = Math.random()*3 + 1
return (rnd > 1.5)? 1:-1;
}
#Override
public boolean equals(MyNeverEqualClass other){
return false;
}
#Override
public int hashCode(){
return stupidHash++;
}
}
public static void main(String[] args){
MyNeverEqualClass a = new MyNeverEqualClass(1);
MyNeverEqualClass b = new MyNeverEqualClass(1);
DupSet<MyNeverEqualClass> set = new DupSet<>();
set.add(a);
set.add(b);
}

ContainsKey of HashMap does not work with a Custom Class as key

I have a custom class MarioState that I want to use in a HashMap. The class represents a possible state in a state space of the Mario game. Below is a simplified version of the class MarioState.
In my HashMap I want to store these states. However, not ever property in the MarioState is something that should be considered when comparing two MarioState's. For example if one MarioState has the stuck property set to true and a distance of 30 and another MarioState also has the stuck property set to true but a different distance value (e.g. 20) then they still should be considered the same.
I know for this to work in my HashMap I have to implement the .equals() and .hashcode() methods, which is what I did (by letting them be automatically generated by the InteliJ IDE).
public class MarioState{
// Tracking the distance Mario has moved.
private int distance;
private int lastDistance;
// To keep track of if Mario is stuck or not.
private int stuckCount;
private boolean stuck;
public MarioState(){
stuckCount = 0;
stuck = false;
distance = 0;
lastDistance = 0;
}
public void update(Environment environment){
// Computing the distance
int tempDistance = environment.getEvaluationInfo().distancePassedPhys;
distance = tempDistance - lastDistance;
lastDistance = tempDistance;
// If Mario hasn't moved for over 25 turns then this means he is stuck.
if(distance == 0){
stuckCount++;
} else {
stuckCount = 0;
stuck = false;
}
if(stuckCount > 25){ stuck = true; }
}
public float calculateReward(){
float reward = 0f;
reward += distance * 2;
if(stuck){ reward += -20; }
return reward;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MarioState that = (MarioState) o;
if (stuck != that.stuck) return false;
return true;
}
#Override
public int hashCode() {
return (stuck ? 1 : 0);
}
}
The problem is however that when running the code some of the keys are considered different when it shouldn't be according to their .equals() and .hashcode() functions. What can possibly cause this? Did I forget something?
The code used when inserting states in the HashMap (additional information can be provided if necessary):
public float[] getActionsQValues(MarioState state){
if(!table.containsKey(state)) {
float[] initialQvalues = getInitialQvalues(state);
table.put(state, initialQvalues);
return initialQvalues;
}
return table.get(state);
}
A screenshot when I'm in debug mode shows my table containing two keys with different values, but the keys itself are the same (but in the HashMap it is considered different).
Your hash code computation and equality comparison are both based on stuck - but that can change over time.
If you mutate an object after adding it as a key within a hash map, in such a way that the hash code changes, then the key will not be found when you later request it - because the hash code that was stored when the key was first added will no longer be the same as its current hash code.
Wherever possible, try to avoid using mutable objects as keys within a map (even a TreeMap which doesn't use the hash code would have the same problem if you changed the object in a way which would change relative ordering). If you must use mutable objects as keys within a map, you should avoid mutating them after adding them as keys.

Issues with Java HashMap and key Object I rolled myself

So, I'm trying to use a HashMap to map my own Object to a String value. My object is below (with some code removed for brevity)
public class RouteHeadsignPair {
String route;
String headsign;
public RouteHeadsignPair(String n_route, String n_headsign) {
route = n_route.toLowerCase();
headsign = n_headsign.toLowerCase();
}
public String getRoute () {
return route;
}
public String getHeadsign() {
return headsign;
}
public boolean equals(RouteHeadsignPair other) {
return(other.getRoute().equals(route) && other.getHeadsign().equals(headsign));
}
public int hashCode() {
return(route.hashCode());
}
}
I'm mapping a bunch of these objects to Strings by loading data from a text file. Later on, based on (independent) user input, I try to query the HashMap using a RouteHeadsignPair Object. containsKey() returns false and get() returns null, as if I had never added the key into the map. But, bizarrely, if I iterate over the map using the below code (where newKey is a RouteHeadsignPair made from user input)
RouteHeadsignPair foundKey = null;
Iterator<RouteHeadsignPair> keysInMap = routeHeadsignToStopIdMap.keySet().iterator();
while(keysInMap.hasNext()) {
RouteHeadsignPair currKey = keysInMap.next();
if(currKey.equals(newKey)) {
System.err.println("Did find a key with an equals() == true!");
foundKey = currKey;
}
}
System.err.println("Value in map? " + routeHeadsignToStopIdMap.containsKey(newKey) + "( hashcode = " + newKey.hashCode() +
", equals = " + newKey.equals(foundKey) + ")");
System.err.println("foundKey in map? " + routeHeadsignToStopIdMap.containsKey(foundKey) + "( hashcode = " + foundKey.hashCode() +
", equals = " + foundKey.equals(newKey) + ")" );
I apologize for the code formatting, it's late and I'm getting cranky
I get the following output
Did find a key with an equals() == true!
and then
Value in map? false( hashcode = 1695, equals = true)
foundKey in map? true( hashcode = 1695, equals = true)
So, if I iterate over the keys and look for keys that return equals(), I do find one, and the hashCode() is the same for both of these. If the hashCode() is the same for newKey and foundKey and foundKey.equals(newKey) returns true, shouldn't HashMap.get(key) return a value and containsKey() return true? What am I doing wrong here?
You're not overriding Object.equals - you're overloading it because of the parameter type. Your diagnostic code calls your overload, but the map code doesn't (as it doesn't know about it).
You need a method with a signature of
public boolean equals(Object other)
If you use the #Override annotation you'll get an error if you fail to override something properly.
You'll need to check whether other is an instance of RouteHeadSignPair first, then cast. If you make the RouteHeadSignPair class final, you won't need to worry about whether or not it's the exact same class, etc.
Note that your hash codes will collide unnecessarily, by the way - if you use both the route and the headSign hashes to generate your hash code, it may help your map lookups to be more efficient. (If there are several instances with the same route but different head signs, it's useful if the map doesn't have to check for equality on all of them when looking up a key.)

Categories