HashCode and Equals in Java [closed] - java

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I understand the basic concept of overriding hashcode and equals in a class , But can anyone give me an example (code) where the equals will fail only because hashcode was not overridden ?
Thanks in advance.

It's not that equals itself will fail - it's that anything that relies on the contract of hashCode and its relationship to equals could fail. Here's an example:
import java.util.*;
final class Person {
private final String name;
public Person(String name) {
// TODO: Nullity prohibition
this.name = name;
}
#Override
public boolean equals(Object other) {
if (other == null || other.getClass() != Person.class) {
return false;
}
return name.equals(((Person)other).name);
}
}
class Test {
public static void main(String[] args) throws Exception {
Person p1 = new Person("Jon");
Person p2 = new Person("Jon");
System.out.println(p1.equals(p2)); // true
Set<Person> people = new HashSet<Person>();
people.add(p1);
people.add(p2);
System.out.println(people.size()); // 2!
}
}
HashSet assumes that because p1.hashCode() isn't the same as p2.hashCode(), the two elements must be unequal, so can both be in the set. That wouldn't happen if hashCode() were appropriately overridden.
Likewise you could have (with the same Person class);
Map<Person, String> map = new HashMap<Person, String>();
map.put(p1, "foo");
System.out.println(map.get(p2)); // null
This would print out "foo" if the two objects returned equal hash codes, as they're meant to - but again, because they don't, the HashMap thinks there's no match.
Eric Lippert's blog post on GetHashCode is a good introduction to this - it's C#-based, but it applies equally to Java.

If your equals is failing it is because you implemented equals wrong.
Here is how to do it correctly:
the answer to What issues should be considered when overriding equals and hashCode in Java?.
but, just for fun here is an example of an equals method that will fail if hashcode is not overridden:
//NEVER DO THIS
#Override
public boolean equals(Object o){
ThisObject obj = (ThisObject)o;
return this.hashCode() == obj.hashCode();
}

Related

Why is .contains returning false when I add a duplicate? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I'm writing a simple method to add an object to an ArrayList if it doesn't already contain it. I'm using the .contains method, but for some reason, when I add a duplicate of an object to the ArrayList, the .contains method returns false, even though I have already added the same object to the ArrayList.
This is my City class:
class City {
private String name, country;
//getters, setters, constructor
}
When I have an ArrayList like this:
List<City> destinations = new ArrayList<>();
with one object added to it
destinations.add(new City("Edmonston", "CA"));
If I check if it contains a duplicate object like this, it returns false
destinations.contains(new City("Edmonston", "CA"))
Here's the full code:
Main method
,
Output
City class
Trip class
Thanks for the help!
See the documentation of List#contains given below:
Returns true if this list contains the specified element. More
formally, returns true if and only if this list contains at least one
element e such that Objects.equals(o, e).
Thus, you need to override equals method in class City. You can do it as follows:
#Override
public boolean equals(Object obj) {
City other = (City) obj;
return Objects.equals(name, other.name) && Objects.equals(country, other.country);
}
In order for contains() to work properly, you have to override the equals() and hashCode() methods in your class. If you don't, the base for equality is that the objects referenced are one and the same.
For example:
City c1 = new City("London", "UK") ;
City c2 = new City("London", "UK") ;
System.out.println(c1.equals(c2)) ; // prints false
c2 = c1 ;
System.out.println(c1.equals(c2)) ; // prints true
EDIT: To override the equals() method, see Arvind Kumar Avinash's answer.
The way the contains method works, according to the documentation, is that it uses Objects.equals(o, e) where o is the object you're checking exists in the ArrayList and e is every element in the array.
Objects.equals(a, b) first checks if either of the arguments are null, and then uses a.equals(b) to check if they're the same.
By default, the equals method in the Object just does a == b, which only returns true if a is the exact same object as b, i.e., a and b both refer to the same location in memory and doesn't care about the variables inside the object.
City c = new City("foo", "bar");
c == new City("foo", "bar") //not true
c == c //true
Because of this default implementation of equals:
City c = new City("foo", "bar");
c.equals(new City("foo", "bar")) //not true
c.equals(c) //true
Therefore, you need to override the equals method in your City class so that you can check whether 2 City objects are equal. Arvind Kumar Avinash's answer provides a way to do this.
However, every collection can implement the contains and similar methods differently. HashSets may use the hash code of objects to compare them. Therefore, it's important to also override the hashCode method for your class.
public int hashCode() {
return Objects.hash(name, country);
}
If you want to insert only one City object in your list, use HashSet and override the equals() and hashCode() methods from Object class in your City class.
public static void main(String[] args) {
Set<City> list = new HashSet<City>();
list.add(new City("Edmonston", "CA"));
list.add(new City("Edmonston", "CA"));
list.add(new City("Edmonston", "CA"));
System.out.println(list); //Edmonston in CA
}
And your City class:
class City {
private String name, country;
public City(String s1, String s2) {
this.name = s1;
this.country = s2;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof City)
return this.name == ((City) obj).name & this.country == ((City) obj).country;
else
return false;
}
#Override
public int hashCode() {
return this.country.hashCode() + this.name.hashCode();
}
#Override
public String toString() {
return this.name + " in " + this.country;
}
}

Assert.assertEquals() fails for custom class objects [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
Even though I have two data objects of a custom class which are equal w.r.t all the variables, assertEquals() method is failing. What am I missing here?
For comparing two objects you need to override the equals() method of the Object class.
When you create two objects of a class, say class A, then the objects are different even if they have all the same variables. This is because the equals method or == both check if the reference of the objects are pointing to the same object.
Object o1 = new A();
Object o2 = new A();
o1.equals(o2);
Here the equals method will return false, even if all the fields are null or even if you assign same values to both the objects.
Object o1 = new A();
Object o2 = o1;
o1.equals(o2);
Here the equals method will return true, because the object is only one, and both o1, o2 references are pointing to same object.
What you can do is override the equals method
public class A {
#Override
public boolean equals(Object obj) {
if (obj==this) return true;
if (obj==null || obj.getClass()!=this.getClass()) return false;
return (this.id==((A) obj).id);
}
// You must also override hashCode() method
}
Here we say objects of class A are equal if they have same id. You can do same for multiple fields.
Comparison to check if its equals are not happens with the help of the equals() function. You need to override this method in your custom class.
public boolean equals(Object obj) { }
Please also make sure you override hashCode() method as well.

I'm getting ClassCastException even though I overriden compareTo() [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I am getting ClassCastException error. This error occurs when I insert the object that is derived from a class I have created.
My code is below:
When I run, I always get ClassCastException error.
Also, the comparator of my class is shown as null in debugger.
I have written a comparator (as far as I know) and overridden necessary methods.
How can I use a Set<> with a class that I have created and use contains() method?
public class Person implements Comparable<Person>
{
int age;
double height;
public Person(int age, double height)
{
this.age = age;
this.height = height;
}
#Override
public int compareTo(Person person)
{
return age - person.age;
}
public boolean equals(Object obj)
{
final Person other = (Person) obj;
if (this.age == other.age)
return true;
return false;
}
public static void main(String[] args)
{
Set<Person> people = new HashSet<>();
Person p1 = new Person(10, 1.00);
Person p2 = new Person(11, 1.10);
Person p3 = new Person(12, 1.20);
Person p4 = new Person(14, 1.40);
people.add(p1);
people.add(p2);
people.add(p3);
people.add(p4);
if(people.contains(12))
System.out.println("contains");
else
System.out.println("does not contain");
}
}
I have managed to get rid of the error. But now, the output is "does not contain".
What I am about to suggest has nothing to do with ClassCastException, which is thrown simply because you are checking contains with an int argument on a set of objects of type Person ... short answer: you can't cast an object to a subclass of which it is not an instance. Even the javadoc says exactly that.
For situations like this, I really like to use Guava predicates. A predicate allows you to apply a boolean condition to any iterable, returning those elements that satisfy the specified condition. In your example, you can define predicates that return the subset of people of whatever age you want.
Predicate<Person> getAgePredicate(final int age) {
return new Predicate<Person>() {
public boolean apply(Person p) { return p.age == age; }
};
}
Set<Person> people = new Hashset<>();
... // populate people
Set<Person> peopleOfAgeTwelve = Sets.filter(people, getAgePredicate(12));
Hope this helps.
I assume that your class Adjacent implements Comparable interface based on your code. And your class contains a method called getID(). So therefore when you override the compareTo() method, you want to make sure that the object comparison makes sense. I'm not sure what your getID() returns. But it seems like integer. So you might want to change the implementation of compareTo() as follows:
#Override
public int compareTo(Adjacent adj) {
return this.getId() - adj.getId();
}
So this way the comparison will return negative/zero/positive depending on the ID comparison of two Adjacent class objects.
Also in your overrided equals() method, the implementation is incorrect because two objects are not necessarily equal even if they have the same hash code. A good example of overriding equals and hashCode methods is given in another SO post. Also, in your case, I think you probably don't even need to override equals method since your class implements Comparable interface already.
Don't call contains with a parameter that's a different type; this behavior is actually documented. The contains() method is a non-generic method for legacy reasons, and it's not safe if you're using a TreeSet. If you implement hashCode() and equals() and switch to a HashSet, your problems will go away.
Do you really need people sorted by age?
Edit: I see what you're trying to do, now. You don't want a Set, you want Map<Integer, Collection<Person>>, or just a single pass loop to look for the given age.
for (Person p : people) {
if (p.age == 12) ...;
}
or
Map<Integer, Set<Person> peopleByAge = new HashMap<Integer, Set<Person>>();
for (Person p : people) {
if (!peopleByAge.contains(p.age)) {
peopleByAge.put(p.age, new TreeSet<Person>();
}
peopleByAge.get(p.age).add(p);
}
if (people.age.containsKey(12)) ...

Weird Set.contains() behavior

I initially started this as a test for a theory-based, best-practices question that I wanted to ask here, but in the process I found some interesting behavior in the java.Set class. Initially, I wanted to know any potential pitfalls of this approach, but now that I can see it doesn't work at all, I'd like to know why.
I have some objects that are containers for database objects for my app. The objects all have unique integer id's, and hashCode() and equals() are defined by the integer ids (for storage in hashsets).
Well, I wanted the ability to check if a hashset contains the object given only the id.
Certainly, I could create a new instance of the object and check that way. But, just for kicks, I wanted to see if I could accomplish it. Of course, this is also trivial with a hashmap, so this is really not an important question, just for fun and knowledge.
So, I made a class, and tried to call contains() on an integer, instead of an instance of the object. Netbeans, of course, gives a fun warning for this
Suspicious call to java.util.Collection.contains:
Given object cannot contain instances of int (expected Person)
Ignoring the error and running the code, I was shocked to find that Java does not even call the equals method. I placed debugging System.out.println()s in my equals method to verify, and yep, it's not even being called.
In the code posted below, the expected output should be (if my theory was correct):
Here
Yes
Here
Yes
or (if my theory was incorrect):
Here
Yes
Here
No
However, the output is:
Here
Yes
No
Notice, there's no "Here" before the "No" proving that the equals method is not even being called.
Can anyone shed light? I was always told to add this to equals() for efficiency:
if (!(obj instanceof Person))
return false;
But if equals() is not even called in such a situation, then that would be pointless.
Here is the SSCCE:
Thanks for your time.
import java.util.LinkedHashSet;
import java.util.Set;
/**
*
* #author Ryan
*/
public class Test7 {
public static void main(String[] args) {
class Person {
public final int id;
public final String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
#Override
public boolean equals(Object obj) {
System.out.println("Here");
if (this == obj)
return true;
if (obj instanceof Person)
return id == ((Person)obj).id;
else if(obj instanceof Integer)
return id == (Integer)obj;
else {
System.out.println("Returning False");
return false;
}
}
#Override
public int hashCode() {
return id;
}
}
Set<Person> set = new LinkedHashSet<Person>();
set.add(new Person(1, "Bob"));
set.add(new Person(2, "George"));
set.add(new Person(3, "Sam"));
if(set.contains(new Person(1, "Bob")))
System.out.println("Yes");
else
System.out.println("No");
if(set.contains(1))
System.out.println("Yes");
else
System.out.println("No");
}
}
This is due to that fact that the comparison is done on the provided object not the elements in the set. From HashSet#contains(Object):
Returns true if this set contains the specified element. More formally, returns true if and only if this set contains an element e such that (o==null ? e==null : o.equals(e)).
So in your example, you would be doing comparison like integer.equals(person). So if your set contains Person objects, the if(obj instanceof Integer) condition will never be checked, but if your set contained Integer objects, that condition would be satisfied and as such would be checked.

Did I override equals and hashcode correctly?

In my most recent question, I was informed that I needed to override my equals and hashcode method (among other things). So I took a bit of time to read a few articles and attempted to come up with a proper implementation.
Here are some of the articles I read:
Hashcode on Wikipedia
StackOverflow Overriding equals and hashcode
StackOverflow Why does hashcode use multiplier 31
All the articles were pretty good. Since this is my first time attempting to do this, I just want to be sure I'm not making some simple (or dumb) mistake.
I will be using name to indicate whether my Person object is equivalent to another Person object. The reason for this, is that all the other variables can vary, but the name will always be unique.
Updated to reflect recommended changes
public class Person {
private String name;
private int p_number;
private String address;
//other variables
public Person(String a_name) {
name = a_name;
}
public String getName() {
return name;
}
//other getters and setters
#Override
public boolean equals(Object o) {
if(o == null)
return false;
if(o == this)
return true;
if(!(o instanceof Person))
return false;
Person p = (Person) o;
return name.equals(p.name));
}
#Override
public int hashCode() {
return name.hashCode();
}
}
My questions are as follows:
Did I implement these methods correctly?
Since name is the only variable that determines uniqueness, do I need to bother checking any of the other variables in hashcode?
I was reading on StackOverflow that 31 was chosen as a good prime number awhile ago, but choosing a larger prime is better now? Can someone confirm or deny this claim? (the claim was made in the third link above)
If I haven't implemented these methods properly, how can I change/improve them?
In equals():
if(name.equals(p.getName()))
return true;
Missing false, and you can just:
// Both are Person instances, no need to use the accessor here
return name.equals(p.name);
As to hashCode(), just return name.hashCode();
Also, can name be null? Your methods don't seem to account for that. (edit: answer: no)
As to your questions:
Since name is the only variable that determines uniqueness, do I need to bother checking any of the other variables in hashcode?
NO, certainly not! If your names are equal but ages different, this would lead to a different hash code for objects which are equal, and this violates the Object contract!
I was reading on StackOverflow that 31 was chosen as a good prime number awhile ago, but choosing a larger prime is better now? Can someone confirm or deny this claim? (the claim was made in the third link above)
This, no idea...
To be more complete about the .equals()/.hashCode() contract, I'll mention a utility class from Guava: Equivalence. An implementation of this abstract class for a given
class can allow you to create Sets, and therefore Maps, with these objects as members (keys) as if they had a different implementation of both of these functions:
Equivalence<MyClass> eq = ....;
Set<Equivalence.Wrapper<MyClass>> set = ...;
set.add(eq.wrap(myClassInstance));
This can be actually very useful in some scenarios...
Your equals needs to return a value in all cases, just change the end part to return the name.equals.
#Override
public boolean equals(Object o) {
...
return name.equals(o.getName());
}
Also your hashcode is actually detrimental, all it does is use the name.hashCode() but then multiply it by 31, better to use the default Java string hashcode from the name directly.
#Override
public int hashCode() {
return name.hashCode();
}

Categories