HashMap not returning values based on key - java

I'm trying to use a HashMap with my class Cell as the key. However, after putting an item into the HashMap, calling contains on the item will return false.
public static void main(String args[]) {
HashMap<Cell, String> map = new HashMap<Cell, String>();
map.put(new Cell(0,0), "Bob");
System.out.println(map.containsKey(new Cell(0,0)));
System.out.println(new Cell(0,0).equals(new Cell(0,0)));
}
This prints out false and true, where it should print true and true, since according to the Map docs containsKey uses .equals(). What am I doing wrong?

This is most likely because you don't have equals() and hashCode() implemented. In Java, the rule of thumb is that if you implement one, you must implement the other. In your case, it's mandatory because HashMap makes use of them.
You created two separate objects with two separate addresses. Without these methods, the JVM has no way of knowing that the objects are "the same."
See http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode()

Consider how a HashMap is implemented. When putting, it first calculates the object hashCode() to figure out which bucket to place the object in. When it tries to retrieve an object, it again gets its hashCode(), identifies to target bucket, goes through the linked list in the bucket, calling equals() against each object. It returns if it finds a match.
In other words, when you use HashMap you need to have a correct and matching implementation of equals() and hashCode().
The default hashCode() method inherited from Object does not correctly return the same hashCode() unless the object references are the same. In your case they are not.

calling new Cell(0,0) several times produce different objects with different hash Codes. You should implement hashCode for Cell class.

You likely forgot to implement the hashcode() function for Cell, which is also required in order to use a user defined class in a HashMap. Here is a simple and generally accurate way to implement a hashcode() function:
int hashcode(){
return (field1.toString()+field2.toString()+...+fieldN.toString()).hashcode();
}
Where field1 to fieldN are the fields in your class. If the fields are primatives (ie int) just take out the toString().

Related

Compare two objects and add them in a Set [duplicate]

This question already has answers here:
Compare two objects with .equals() and == operator
(16 answers)
Closed 9 years ago.
I have two java objects that are instantiated from the same class.
MyClass myClass1 = new MyClass();
MyClass myClass2 = new MyClass();
If I set both of their properties to the exact same values and then verify that they are the same
if(myClass1 == myClass2){
// objects match
...
}
if(myClass1.equals(myClass2)){
// objects match
...
}
However, neither of these approaches return a true value. I have checked the properties of each and they match.
How do I compare these two objects to verify that they are identical?
You need to provide your own implementation of equals() in MyClass.
#Override
public boolean equals(Object other) {
if (!(other instanceof MyClass)) {
return false;
}
MyClass that = (MyClass) other;
// Custom equality check here.
return this.field1.equals(that.field1)
&& this.field2.equals(that.field2);
}
You should also override hashCode() if there's any chance of your objects being used in a hash table. A reasonable implementation would be to combine the hash codes of the object's fields with something like:
#Override
public int hashCode() {
int hashCode = 1;
hashCode = hashCode * 37 + this.field1.hashCode();
hashCode = hashCode * 37 + this.field2.hashCode();
return hashCode;
}
See this question for more details on implementing a hash function.
You need to Override equals and hashCode.
equals will compare the objects for equality according to the properties you need and hashCode is mandatory in order for your objects to be used correctly in Collections and Maps
You need to implement the equals() method in your MyClass.
The reason that == didn't work is this is checking that they refer to the same instance. Since you did new for each, each one is a different instance.
The reason that equals() didn't work is because you didn't implement it yourself yet. I believe it's default behavior is the same thing as ==.
Note that you should also implement hashcode() if you're going to implement equals() because a lot of java.util Collections expect that.
You have to correctly override method equals() from class Object
Edit: I think that my first response was misunderstood probably because I was not too precise. So I decided to to add more explanations.
Why do you have to override equals()? Well, because this is in the domain of a developer to decide what does it mean for two objects to be equal. Reference equality is not enough for most of the cases.
For example, imagine that you have a HashMap whose keys are of type Person. Each person has name and address. Now, you want to find detailed bean using the key. The problem is, that you usually are not able to create an instance with the same reference as the one in the map. What you do is to create another instance of class Person. Clearly, operator == will not work here and you have to use equals().
But now, we come to another problem. Let's imagine that your collection is very large and you want to execute a search. The naive implementation would compare your key object with every instance in a map using equals(). That, however, would be very expansive. And here comes the hashCode(). As others pointed out, hashcode is a single number that does not have to be unique. The important requirement is that whenever equals() gives true for two objects, hashCode() must return the same value for both of them. The inverse implication does not hold, which is a good thing, because hashcode separates our keys into kind of buckets. We have a small number of instances of class Person in a single bucket. When we execute a search, the algorithm can jump right away to a correct bucket and only now execute equals for each instance. The implementation for hashCode() therefore must distribute objects as evenly as possible across buckets.
There is one more point. Some collections require a proper implementation of a hashCode() method in classes that are used as keys not only for performance reasons. The examples are: HashSet and LinkedHashSet. If they don’t override hashCode(), the default Object
hashCode() method will allow multiple objects that you might consider "meaningfully
equal" to be added to your "no duplicates allowed" set.
Some of the collections that use hashCode()
HashSet
LinkedHashSet
HashMap
Have a look at those two classes from apache commons that will allow you to implement equals() and hashCode() easily
EqualsBuilder
HashCodeBuilder
1) == evaluates reference equality in this case
2) im not too sure about the equals, but why not simply overriding the compare method and plant it inside MyClass?

Should hashCode() only use the subset of immutable fields of those used in equals()?

Situation
I needed to overwrite equals() and as it is recommended I also overwrote the hashCode() method using the same fields. Then, when I was looking at a set, that contained only the one object I got the frustrating result of
set.contains(object)
=> false
while
set.stream().findFirst().get().equals(object)
=> true
I understand now, that this is due to changes that were made to object after it was added to set which again changed its hashCode. contains then looks at the wrong key and can't find the object.
My requirements for the implementation are
mutable fields are needed to correctly implement equals()
use these objects safely in hash-based Collections or Maps such ash HashSet even if they are prone to changes.
which conflicts with the convention that
equals() and hashCode() should use the same fields in order to avoid surprises (as argued here: https://stackoverflow.com/a/22827702).
Question
Are there any dangers to using only a subset of fields which are used in equals() to calculate hashCode() instead of using all?
More specifically this would mean: equals() uses a number of fields of the object whereas hashCode() only uses those fields that are used in equals() and that are immutable.
I think this should be okay, because
the contract is fullfilled: equal objects will produce the same hashCode, while the same hashCode does not necesairly mean that the objects are the same.
The hashCode of an object stays the same, even if an object is exposed to changes and therefore will be found in a HashSet before and after those changes.
Related posts that helped me understand my problem but not how to solve it: What issues should be considered when overriding equals and hashCode in Java? and Different fields for equals and hashcode
It's ok for hashCode() to use a subset of the fields that equals() uses, although it may possibly give you a slight performance drop.
Your problem seems to be caused by modifying the object, while still inside the set, in a way that alters the functioning of hashCode() and/or equals(). Whenever you add an object to a HashSet (or as the key in a HashMap), you must not subsequently modify any fields of that object that are used by equals() and/or hashCode(). Ideally, all fields used by equals() should be final. If they can't be, you must treat them as though they are final whilst the object is in the set.
The same goes for TreeSet/TreeMap, too, but applies to fields used by compareTo().
If you really need to modify the fields that are used by equals() (or by compareTo() in the case of a TreeSet/TreeMap), you must:
First, remove that object from the set;
Then modify the object;
And finally add it back to the set.
The contract would indeed be fulfilled. The contract imposes that .equal() objects have ALWAYS the same .hashCode(). The opposite doesn't have to be true and I wonder with the obsession of some people and IDEs to apply exactly that practice. If this was possible for all possible combinations, then you would discover the perfect hash function.
BTW, IntelliJ offers a nice wizard when generating hashCode and equals by treating those two methods separately and allowing to differentiate your selection. Obviously, the opposite, aka offering more fields in the hashCode() and less fields in the equals() would violate the contract.
For HashSet and similar collections/maps, it's a valid solution to have hashCode() use only a subset of the fields from the equals() method. Of course, you have to think about how useful the hash code is to reduce collisions in the map.
But be aware that the problem comes back if you want to use ordered collections like TreeSet. Then you need a comparator that never gives collisions (returns zero) for "different" objects, meaning that the set can only contain one of the colliding elements. Your equals() description implies that multiple objects will exist that differ only in the mutable fields, and then you lose:
Including the mutable fields in the compareTo() method can change the comparison sign, so that the object needs to move to a different branch in the tree.
Excluding the mutable fields in the compareTo() method limits you to have maximum one colliding element in the TreeSet.
So I'd strongly recommend to think about your object class'es concept of equality and mutability again.
That's perfectly valid to me. Suppose you have a Person:
final int name; // used in hashcode
int income; // name + income used in equals
name decides where the entry will go (think HashMap) or which bucket will be chosen.
You put a Person as a Key inside HashMap : according to hashcode it goes to some bucket, second for example. You upgrade the income and search for that Person in the map. According to hashcode it must be in the second bucket, but according to equals it's not there:
static class Person {
private final String name;
private int income;
public Person(String name) {
super();
this.name = name;
}
public int getIncome() {
return income;
}
public void setIncome(int income) {
this.income = income;
}
public String getName() {
return name;
}
#Override
public int hashCode() {
return name.hashCode();
}
#Override
public boolean equals(Object other) {
Person right = (Person) other;
return getIncome() == right.getIncome() && getName().equals(right.getName());
}
}
And a test:
HashSet<Person> set = new HashSet<>();
Person bob = new Person("bob");
bob.setIncome(100);
set.add(bob);
Person sameBob = new Person("bob");
sameBob.setIncome(200);
System.out.println(set.contains(sameBob)); // false
What you are missing I think is the fact that hashcode decides a bucket where an entry goes (there could be many entries in that bucket) and that's the first step, but equals decides if that is well, an equal entry.
The example that you provide is perfectly legal; but the one you linked is the other way around - it uses more fields in hashcode making it thus incorrect.
If you understand these details that first hashcode is used to understand where and Entry might reside and only later all of them (from the subset or bucket) are tried to be found via equal - your example would make sense.

Collections break

From this link
Name objects are immutable. All other things being equal, immutable
types are the way to go, especially for objects that will be used as
elements in Sets or as keys in Maps. These collections will break if
you modify their elements or keys while they're in the collection.
How do we know that the class "Name" is immutable? (class name visible in the link above mentioned)
What do they actually mean with "the collections will break if you modify their elements?"
Thanks in advance.
Because with mutable classes, you can change the properties based which they are organized/ordered in the Collections, and the holder class would not know about it.
Think that you could do:
public class Name implements Comparable<Name> {
private String firstname = null
// getters and setters
public int compareTo(Name name) {
// Compare based in firstName
}
}
And then:
Name name1 = new Name("John");
Name name2 = new Name("Mike");
SortedSet<Name> set = new TreeSet<Name>();
set.add(name1);
set.add(name2);
name1.setFirstName("Ralph");
Now, is set ordered or is it not?
In a similar way, changes that affect the hashCode of the instance break HashMap and similars, because the first that does these classes when inserting/retrieving objects is to use a specific bucket based in that value.
What they mean is, lookups based on object will be failed.
For example:
mylist.get(myObject);
will fail because the object reference you have will be different (due to modifications) from the one you are using to do get(...) call.
HashSet and HashMap rely on the contract for equals() and hashCode described in the javadoc for java.lang.Object. That means that for two objects being equal accorrding to equals() the calculated hashCode() must also be equal.
If the hashCode() for a object in a Set or Map changes during the time the object is in the Set or Map the implementation will not find the object as it is saved in the bucket for the old hashCode().
Therefore changing the hashCode() while an object is in a Set or Map is a really bad idea.
From the docs on Map
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map. A special case of this prohibition is that it is not permissible for a map to contain itself as a key. While it is permissible for a map to contain itself as a value, extreme caution is advised: the equals and hashCode methods are no longer well defined on such a map.
From the docks on Set
Note: Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set. A special case of this prohibition is that it is not permissible for a set to contain itself as an element.
Lookups are done with .equals on the keys, if the keys are mutable the lookup will fail.

How to compare two java objects [duplicate]

This question already has answers here:
Compare two objects with .equals() and == operator
(16 answers)
Closed 9 years ago.
I have two java objects that are instantiated from the same class.
MyClass myClass1 = new MyClass();
MyClass myClass2 = new MyClass();
If I set both of their properties to the exact same values and then verify that they are the same
if(myClass1 == myClass2){
// objects match
...
}
if(myClass1.equals(myClass2)){
// objects match
...
}
However, neither of these approaches return a true value. I have checked the properties of each and they match.
How do I compare these two objects to verify that they are identical?
You need to provide your own implementation of equals() in MyClass.
#Override
public boolean equals(Object other) {
if (!(other instanceof MyClass)) {
return false;
}
MyClass that = (MyClass) other;
// Custom equality check here.
return this.field1.equals(that.field1)
&& this.field2.equals(that.field2);
}
You should also override hashCode() if there's any chance of your objects being used in a hash table. A reasonable implementation would be to combine the hash codes of the object's fields with something like:
#Override
public int hashCode() {
int hashCode = 1;
hashCode = hashCode * 37 + this.field1.hashCode();
hashCode = hashCode * 37 + this.field2.hashCode();
return hashCode;
}
See this question for more details on implementing a hash function.
You need to Override equals and hashCode.
equals will compare the objects for equality according to the properties you need and hashCode is mandatory in order for your objects to be used correctly in Collections and Maps
You need to implement the equals() method in your MyClass.
The reason that == didn't work is this is checking that they refer to the same instance. Since you did new for each, each one is a different instance.
The reason that equals() didn't work is because you didn't implement it yourself yet. I believe it's default behavior is the same thing as ==.
Note that you should also implement hashcode() if you're going to implement equals() because a lot of java.util Collections expect that.
You have to correctly override method equals() from class Object
Edit: I think that my first response was misunderstood probably because I was not too precise. So I decided to to add more explanations.
Why do you have to override equals()? Well, because this is in the domain of a developer to decide what does it mean for two objects to be equal. Reference equality is not enough for most of the cases.
For example, imagine that you have a HashMap whose keys are of type Person. Each person has name and address. Now, you want to find detailed bean using the key. The problem is, that you usually are not able to create an instance with the same reference as the one in the map. What you do is to create another instance of class Person. Clearly, operator == will not work here and you have to use equals().
But now, we come to another problem. Let's imagine that your collection is very large and you want to execute a search. The naive implementation would compare your key object with every instance in a map using equals(). That, however, would be very expansive. And here comes the hashCode(). As others pointed out, hashcode is a single number that does not have to be unique. The important requirement is that whenever equals() gives true for two objects, hashCode() must return the same value for both of them. The inverse implication does not hold, which is a good thing, because hashcode separates our keys into kind of buckets. We have a small number of instances of class Person in a single bucket. When we execute a search, the algorithm can jump right away to a correct bucket and only now execute equals for each instance. The implementation for hashCode() therefore must distribute objects as evenly as possible across buckets.
There is one more point. Some collections require a proper implementation of a hashCode() method in classes that are used as keys not only for performance reasons. The examples are: HashSet and LinkedHashSet. If they don’t override hashCode(), the default Object
hashCode() method will allow multiple objects that you might consider "meaningfully
equal" to be added to your "no duplicates allowed" set.
Some of the collections that use hashCode()
HashSet
LinkedHashSet
HashMap
Have a look at those two classes from apache commons that will allow you to implement equals() and hashCode() easily
EqualsBuilder
HashCodeBuilder
1) == evaluates reference equality in this case
2) im not too sure about the equals, but why not simply overriding the compare method and plant it inside MyClass?

Java HashSet is allowing dupes; problem with comparable?

I've got a class, "Accumulator", that implements the Comparable compareTo method, and I'm trying to put these objects into a HashSet.
When I add() to the HashSet, I don't see any activity in my compareTo method in the debugger, regardless of where I set my breakpoints. Additionally, when I'm done with the add()s, I see several duplicates within the Set.
What am I screwing up, here; why is it not Comparing, and therefore, allowing the dupes?
Thanks,
IVR Avenger
What am I screwing up, here?
HashSet is based on hashCode(), not on compareTo(). You may be confusing it with TreeSet. In both cases, be sure to also implement equals() in a manner that is consistent with the other method.
You need to correctly implement hashCode() and equals().
You must override hashCode and return a number based on the values in your class such that any two equal objects have the same hashcode.
HashSet uses the hashCode() and equals() methods to prevent duplicates from being added. First, it gets the hash code of the object you want to add. Then, it finds the corresponding bucket for that hash code and iterates through each object in that bucket, using the equals() method to see if any identical objects already exist in the set.
Your debugger is not breaking on compareTo() because it is never used with HashSet!
The rules are:
If two objects are equal, then their hash codes
must be equal.
But if two objects' hash codes
are equal, then this doesn't mean
the objects are equal! It could be
that the two objects just happen to have the same hash.
When hashCode return different values for 2 objects, then equal is not used. Btw, compareTo has nothing to do with hashing collections :) but sorted collections
Your objects are Comparable, and probably you've implemented equals() too, but HashSets deal with object hashes, and odds are you haven't implemented hashCode() (or your implementation of hashCode() doesn't return the same hash for two objects that are (a.equals(b) == true).
One thing which people tends to ignore which result in a huge mistake.
While defining equals method always take the parameter as object class and then conver the object to your desired class.
For eg
public bolean equals(Object aSong){
if(!(aSoneg instanceof Song)){
return false;
}
Song s=(Song) aSong;
return getTitle().equals(s.getTitle());
}
If u pass write Song aSong instead of Object aSong your equals method will never get called.
Hope this helps
HashSet uses hashCode and equals. TreeSet uses the Comparable interface. Note: if you decide to override either hashcode or equals, you should always override the other.
When ever you create an object of class Accumulator it takes new space in JVM and returns unique hashCode every time you add an object in hashSet. It does not depends upon the value of the object because you have not overridden hashCode() method hence it will call Object class hashCode() method which will return unique hashCode with every object created in your program.
Solution:
Override hashCode() and equals() method and apply your logic depending upon the properties of your class. Be sure to read equals and hashcode contract
http://www.ibm.com/developerworks/java/library/j-jtp05273/index.html

Categories

Resources