Why are these two instances not `equal()`? [duplicate] - java

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why .equals method is failing on two same value objects?
This is really simple but I'm obviously missing something pretty big here.
Cat cat1 = new Cat("bob");
Cat cat2 = new Cat("bob");
System.out.println(cat1 == cat2);
//false since these references point to diferent objects
System.out.println(cat1.equals(cat2));
//returns false also??
Cat is just a simple class that only has a name.
What is going on here, how does equals() work? I was under the impression that it compared all the fields of the object. It seems that is not the case.
Do I need to overide it for all my classes?

Yes.
java.lang.Object provides very basic implementations of equals() and hashCode(). In particular, they don't go around reflecting on the type of the instance, which would (1) be dreadfully slow, and (2) carry a significant risk of comparing fields that you for various reasons don't want to compare in an equality comparison.
If you want equals() and hashCode() to actually be useful for comparing value equality (rather than reference equality which == does), you'll need to implement both within your own type.
Note that it's not enough to implement just equals(); while technically that will "work", it has the potential to lead to all kinds of weirdness. The simple rule of thumb is: neither or both, but never only one. And they must work on the same fields; if equals() says two instances are equal, then calling hashCode() on both must return the same value (also see the hashCode() contract).
It's also usually a good idea to override toString() with code to provide a meaningful description of the object in question. While not strictly needed, you only need to hit your head against this once in the debugger to realize the value. (Thanks #JonTaylor for mentioning this highly useful, related tidbit.)
And it's .NET that calls it GetHashCode(), while Java uses only hashCode() as the function name...

You need to override equals inside your Cat class. Default equals compares objects on references.
class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
#Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (!(obj instanceof Cat))
return false;
Cat c = (Cat) obj;
return this.name == null ? false : this.name.equals(c.name);
}
#Override
public int hashCode() {
return this.name == null ? 31 : this.name.hashCode();
}
#Override
public String toString() {
return "Cat Name :" + name;
}
}
References
equals
hashCode
toString

The equals() provided by java.lang.object compares, simply speaking, a unique identifier for the object, though not entirely accurate you can think of it as a memory location, so it will only be true if you compare an object with itself (i.e. two references to the same object in memory)
You need to implement your own equals() method in your Cat class:
class Cat
{
String name;
#Override
public boolean equals(Cat other)
{
if (this.name.equals(other.name))
return true;
return false;
}
}
It would be wise to override hashCode() also, unless this is just a very basic application for homework or something. Also toString() can be useful to override as well.
http://docs.oracle.com/javase/tutorial/java/IandI/objectclass.html

From [Java Doc]
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).
Without overriding the equals() method, the objects are different
Hence
System.out.println(cat1.equals(cat2)); // is false

That is because the == compare references and java.lang.Object.equals() translates to this==o thus return same as == in your case
In the case above you are using new operator to create two different objects hence both return false.
If you want .equals() to work as you are expecting, then override theequals() in your Cat class.

Related

Java equals() for Objects (Super&Subclasses)

Hi i'm just beginner learning about abstract classes & interfaces.
Everything we build our prof is testing by creating clones and comparing objects.
I've learned overriding the equals() method the detailed way…
#Override
public boolean equals(Object obj){
if (this == obj){
return true;
}
else if (obj == null){
return false;
}
...
else {
Obj x = (*Superclass*)obj;
…
}
I was now wondering if I could replace this long way by a short Version where I change the toString method, do a hashCode of the toString method and compare the hashCodes of my Objects (original & clone).
Would this be ok to do or is there any reason i shouldn't do it?
Would I be able to inherit the toString, hashCode and equals method and just adjust the clone() in subclasses if we assume that the subclasses use the same variables?
My idea was following
public abstract *Superclass*{
public String name; //would be private in org. code
public int hp; //would be private in org. code
public Superclass(){
}
#Override
public Superclass clone(){
return this; //(not sure if this is ok to use)
}
#Override
public String toString(){
Class temp = getClass();
return temp.getName() + this.name + " " + this.hp;
}
#Override
public int hashCode(){
int hcModify = 10;
int hcCurrent = this.toString().hashCode();
return hcModify * hcCurrent;
}
#Override
public boolean equals(Object obj){
return this.hashCode() == obj.hashCode())
}
}
So the first thing to note is that your equals method will throw an error if obj is null - you can't use any . operators on null.
Your clone method is dangerous if there's mutability in play - mutability means "values can be changed". Because it just returns a reference, changes will be reflected in both the original and "cloned" values (because they're the same.) This is not what most developers would expect. (I suggest looking up deep vs. shallow clones, which is related.)
x = new Thing()
y = thing.clone()
x.changeInSomeWay()
//is y now also changed?
The method of using hash codes for equality is not necessarily good or bad - it depends on the relation of the object to its hash and toString functions, and if there are colisions. Some objects will have hash or toString colisions, where different objects will have the same hash or string representations - particularly large or complex objects, where those representations don't include all of the data that you'd want to be reflected in an equality check.
Yours is actually an example of this. You're using an int hashcode, which only has 2^32 (or whatever) possible values, while Strings have, in principle, infinite possible values; by the pigeonhole principal, there must therefor be multiple objects with different names but the same hashcode.
In general, it's not a safe practice, and can lead to weird, difficult to diagnose errors.
I'm not sure why you're multiplying by 10?

hashCode method in case equals method supports wildchar

I have a class, whose objects will be stored in hashmap, hashset.
I need to support wildchar equality for the fields therefore I have added that check in equals method. Now, how can I build my hashcode method based on this paramter? i.e. if I calculate as shown in code below, I will get different hashcode for 2 objects that pass the equality check of my equals method.
For e.g. consider following class:
public class Person {
private String fname = "";
private String lname = "";
private String profession = "";
// getters & setters
#Override
public boolean equals(Object obj) {
if (fname == null) {
if (other.fname != null)
return false;
} else if (!fname.equals(other.fname) && !other.fname.equals("*") && !fname.equals("*"))
return false;
return true;
// similar for other fields
}
#Override
public int hashCode() {
return Objects.hashCode(this.fname, this.lname , this.profession);
}
}
EDIT:
As pointed out in some of the comments that there should be a separate method to check wildchar equality. The limitation with this approach is I won't be able to get the correct results for methods like set.contains as they check for object.equals internally. So my set.contains will return false, even if my objects passes the equality test of my separate wildchar equality method.
Using the same logic in hashCode() is not possible as we don't compute the hash code according to a compared object but we compute it according to the current object state.
You could return a constant value in the hashCode() method but it would be not efficient and so it doesn't sound a good idea.
Your original issue comes from the fact that you want to give a responsibility not designed to be into the equals() method : defining the equality beyond the actual state of the objects (that is actual field values).
So you should not use the equals() method to perform the wildcard comparison but instead of write a dedicated method for.

Is it a bad practice to return super.equals and super.hashcode in a class?

Let's say i have this class:
public class Person {
private Integer idFromDatabase;
private String name;
//Getters and setters
}
The field idFromDatabase is the attribute that should be verified in equals and used to create the hashCode. But sometimes, i am working with a list of People in memory, and have not yet stored the objects on the database, so the idFromDatabase is null for all objects, which would cause hashCode to return the same value for every object.
I solved this issue by adding the following to equals and hashCode metods:
if(idFromDatabase == null) return super.equals(o);
and
if(idFromDatabase == null) return super.hashCode();
It worked, but is it safe? Can i do it for every class that relies on a database field for equality check?
if(idFromDatabase == null) return super.equals(o); is incorrect as super's equals (if implemented correctly) does a getClass() check, which will of course be different, thus super.equals will always be false.
As already noted by #Jeroen Vannevel, if you are likely to end up with having 2 or more objects not stored in database holding the exact same information, then this technique will not help you in identifying this.
#Solver is also quite true in that a subclass is meant to have different behavior than its superclass, so you shouldn't return that they're equal.
However, in your particular example, you are just extending the Object class, so your assumption that it is safe is true (if we exclude the possibility of having 2 not-yet-persisted same Persons in memory).
Object provides the most basic equals method:
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).
The hashCode method of Object:
As much as is reasonably practical, [...] does return distinct integers for distinct objects
These definitions make it clear that if you're only extending Object, then this technique is safe.
From your description I'm inferring that when comparing two People objects:
If both have an ID, they are equal if they have the same ID, even if they have different names
Otherwise, they are only equal if they are the same instance.
If that's correct, then:
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (this.idFromDatabase == null)
return false;
if (! (obj instanceof People))
return false;
People that = (People)obj;
if (that.idFromDatabase == null)
return false;
return this.idFromDatabase.equals(that.idFromDatabase);
}
#Override
public int hashCode() {
// Use super.hashCode to distribute objects without an idFromDatabase
return (this.idFromDatabase != null ? this.idFromDatabase.hashCode() : super.hashCode());
}
There are a few problems with your reasoning.
equals and hashcode are not subtype-friendly so it doesn't make sense to start thinking about super calls,
super is Object anyway so it's equals and hashcode are useless in this context.
What if you have two Person objects referring to the same person, but only one is stored in the database. Are they the same or different?
One universal solution is to make two classes
Person which stores a 'local' person. Doesn't contain idFromDatabase,
StoredPerson which contains idFromDatabase and a Person (or all fields of Person, but this is harder to maintain)
This way, at least equals and hashcode are well-defined and well-behaved at all times.
Implementation and usage
If you use any kind of Set/Map to store people, you now have two of them. When you save new Persons to database, you remove them from the 'local' Set/Map, wrap them in StoredPerson, and put them in the 'database' Set/Map.
If you want a searchable list of all people, make one with all Persons from both datasets into one. When you find a Person you're interested in and want to retrieve the idFromDatabase, if any, then you'd do good to prepare a map from Person to StoredPerson beforehand.
Thus you need at least,
Set<Person> localPeople = new HashSet<>();
Map<Person, StoredPerson> storedPeople = new HashMap<>();
and something like this:
void savePerson(Person person) {
synchronized (lockToPreserveInvariants) {
int id = db.insert(person);
StoredPerson sp = new StoredPerson(id, person);
localPeople.remove(person);
storedPeople.put(person, sp);
}
}

Java .equals() instanceof subclass? Why not call superclass equals instead of making it final?

It is stated in Object's .equals(Object) javadoc:
It is symmetric: for any non-null reference values x and y,
x.equals(y) should return true if and only if y.equals(x) returns
true.
Almost everywhere in example code I see overridden .equals(Object) method which uses instanceof as one of the first tests, for example here: What issues / pitfalls must be considered when overriding equals and hashCode?
public class Person {
private String name;
private int age;
public boolean equals(Object obj) {
if (obj == null)
return false;
if (obj == this)
return true;
if (!(obj instanceof Person))
return false;
...
}
}
Now with class SpecialPerson extends Person having in equals:
if (!(obj instanceof SpecialPerson))
return false;
we con not guarantee that .equals() is symmetric.
It has been discussed for example here: any-reason-to-prefer-getclass-over-instanceof-when-generating-equals
Person a = new Person(), b = new SpecialPerson();
a.equals(b); //sometimes true, since b instanceof Person
b.equals(a); //always false
Maybe I should add in the beginning of SpecialPerson's equals direct call to super?
public boolean equals(Object obj) {
if( !obj instanceof SpecialPerson )
return super.equals(obj);
...
/* more equality tests here */
}
A lot of the examples use instanceof for two reasons: a) it folds the null check and type check into one or b) the example is for Hibernate or some other code-rewriting framework.
The "correct" (as per the JavaDoc) solution is to use this.getClass() == obj.getClass(). This works for Java because classes are singletons and the VM guarantees this. If you're paranoid, you can use this.getClass().equals(obj.getClass()) but the two are really equivalent.
This works most of the time. But sometimes, Java frameworks need to do "clever" things with the byte code. This usually means they create a subtype automatically. Since the subtype should be considered equal to the original type, equals() must be implemented in the "wrong" way but this doesn't matter since at runtime, the subtypes will all follow certain patterns. For example, they will do additional stuff before a setter is being called. This has no effect on the "equalness".
As you noticed, things start to get ugly when you have both cases: You really extend the base types and you mix that with automatic subtype generation. If you do that, you must make sure that you never use non-leaf types.
You are missing something here. I will try to highlight this:
Suppose you have Person person = new Person() and Person personSpecial = new SpecialPerson() then I am sure you would not like these two objects to be equal. So, its really working as required, the equal must return false.
Moreover, symmetry specifies that the equals() method in both the classes must obey it at the same time. If one equals return true and other return false, then I would say the flaw is in the equals overriding.
Your attempt at solving the problem is not correct. Suppose you have 2 subclasss SpecialPerson and BizarrePerson. With this implementation, BizarrePerson instances could be equal to SpecialPerson instances. You generally don't want that.
don't use instanceof. use this.getClass() == obj.getClass() instead. then you are checking for this exact class.
when working with equalsyou should always use the hashCode and override that too!
the hashCode method for Person could look like this:
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
and use it like this in your equals method:
if (this.hashCode() != obj.hashCode())
{
return false;
}
A type should not consider itself equal to an object of any other type--even a subtype--unless both objects derive from a common class whose contract specifies how descendants of different types should check for equality.
For example, an abstract class StringyThing could encapsulate strings, and provide methods to do things like convert to a string or extract substrings, but not impose any requirements on the backing format. One possible subtype of StringyThing, for example, might contain an array of StringyThing and encapsulate the value of the concatenation of all those strings. Two instances of StringyThing would be defined as equal if conversion to strings would yield identical results, and comparison between two otherwise-indistinguishable StringyThing instances whose types knew nothing about each other may have to fall back on that, but StringyThing-derived types could include code to optimize various cases. For example, if one StringyThing represents "M repetitions of character ch" and another represents "N repetitions of the string St", and the latter type knows about the first, it could check whether St contains nothing but M/N repetitions of the character ch. Such a check would indicate whether or not the strings are equal, without having to "expand out" either one of them.

Why should I override hashCode() when I override equals() method?

Ok, I have heard from many places and sources that whenever I override the equals() method, I need to override the hashCode() method as well. But consider the following piece of code
package test;
public class MyCustomObject {
int intVal1;
int intVal2;
public MyCustomObject(int val1, int val2){
intVal1 = val1;
intVal2 = val2;
}
public boolean equals(Object obj){
return (((MyCustomObject)obj).intVal1 == this.intVal1) &&
(((MyCustomObject)obj).intVal2 == this.intVal2);
}
public static void main(String a[]){
MyCustomObject m1 = new MyCustomObject(3,5);
MyCustomObject m2 = new MyCustomObject(3,5);
MyCustomObject m3 = new MyCustomObject(4,5);
System.out.println(m1.equals(m2));
System.out.println(m1.equals(m3));
}
}
Here the output is true, false exactly the way I want it to be and I dont care of overriding the hashCode() method at all. This means that hashCode() overriding is an option rather being a mandatory one as everyone says.
I want a second confirmation.
It works for you because your code does not use any functionality (HashMap, HashTable) which needs the hashCode() API.
However, you don't know whether your class (presumably not written as a one-off) will be later called in a code that does indeed use its objects as hash key, in which case things will be affected.
As per the documentation for Object class:
The general contract of hashCode is:
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
Because HashMap/Hashtable will lookup object by hashCode() first.
If they are not the same, hashmap will assert object are not the same and return not exists in the map.
The reason why you need to #Override neither or both, is because of the way they interrelate with the rest of the API.
You'll find that if you put m1 into a HashSet<MyCustomObject>, then it doesn't contains(m2). This is inconsistent behavior and can cause a lot of bugs and chaos.
The Java library has tons of functionalities. In order to make them work for you, you need to play by the rules, and making sure that equals and hashCode are consistent is one of the most important ones.
Most of the other comments already gave you the answer: you need to do it because there are collections (ie: HashSet, HashMap) that uses hashCode as an optimization to "index" object instances, an those optimizations expects that if: a.equals(b) ==> a.hashCode() == b.hashCode() (NOTE that the inverse doesn't hold).
But as an additional information you can do this exercise:
class Box {
private String value;
/* some boring setters and getters for value */
public int hashCode() { return value.hashCode(); }
public boolean equals(Object obj) {
if (obj != null && getClass().equals(obj.getClass()) {
return ((Box) obj).value.equals(value);
} else { return false; }
}
}
The do this:
Set<Box> s = new HashSet<Box>();
Box b = new Box();
b.setValue("hello");
s.add(b);
s.contains(b); // TRUE
b.setValue("other");
s.contains(b); // FALSE
s.iterator().next() == b // TRUE!!! b is in s but contains(b) returns false
What you learn from this example is that implementing equals or hashCode with properties that can be changed (mutable) is a really bad idea.
It is primarily important when searching for an object using its hashCode() value in a collection (i.e. HashMap, HashSet, etc.). Each object returns a different hashCode() value therefore you must override this method to consistently generate a hashCode value based on the state of the object to help the Collections algorithm locate values on the hash table.

Categories