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

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?

Related

Why does Java need equals() if there is hashCode()?

If two objects return same hashCode, doesn't it mean that they are equal? Or we need equals to prevent collisions?
And can I implement equals by comparing hashCodes?
If two objects have the same hashCode then they are NOT necessarily equal. Otherwise you will have discovered the perfect hash function. But the opposite is true - if the objects are equal, then they must have the same hashCode.
hashCode and Equals are different information about objects
Consider the analogy to Persons where hashcode is the Birthday,
in that escenario, you and many other people have the same b-day (same hashcode), all you are not the same person however..
Why does Java need equals() if there is hashCode()?
Java needs equals() because it is the method through which object equality is tested by examining classes, fields, and other conditions the designer considers to be part of an equality test.
The purpose of hashCode() is to provide a hash value primarily for use by hash tables; though it can also be used for other purposes. The value returned is based on an object's fields and hash codes of its composite and/or aggregate objects. The method does not take into account the class or type of object.
The relationship between equals() and hashCode() is an implication.
Two objects that are equal implies that the have the same hash code.
Two objects having the same hash code does not imply that they are equal.
The latter does not hold for several reasons:
There is a chance that two distinct objects may return the same hash code. Keep in mind that a hash value folds information from a large amount of data into a smaller number.
Two objects from different classes with similar fields will most likely use the same type of hash function, and return equal hash values; yet, they are not the same.
hashCode() can be implementation-specific returning different values on different JVMs or JVM target installations.
Within the same JVM, hashCode() can be used as a cheap precursor for equality by testing for a known hash code first and only if the same testing actual equality; provided that the equality test is significantly more expensive than generating a hash code.
And can I implement equals by comparing hashCodes?
No. As mentioned, equal hash codes does not imply equal objects.
The hashCode method as stated in the Oracle Docs is a numeric representation of an object in Java. This hash code has limited possible values (represented by the values which can be stored in an int).
For a more complex class, there is a high possibility that you will find two different objects which have the same hash code value. Also, no one stops you from doing this inside any class.
class Test {
#Override
public int hashCode() {
return 0;
}
}
So, it is not recommended to implement the equals method by comparing hash codes. You should use them for comparison only if you can guarantee that each object has an unique hash code. In most cases, your only certainty is that if two objects are equal using o1.equals(o2) then o1.hashCode() == o2.hashCode().
In the equals method you can define a more complex logic for comparing two objects of the same class.
If two objects return same hashCode, doesn't it mean that they are equal?
No it doesn't mean that.
The javadocs for Object state this:
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. ...
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.
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCodemethod on
each of the two objects must produce distinct integer results. ...
Note the highlighted statement. It plainly says "No" to your question.
There is another way to look at this.
The hashCode returns an int.
There are only 232 distinct values that an int can take.
If a.hashCode() == b.hashCode() implies a.equals(b), then there can be only 232 distinct (i.e. mutually unequal) objects at any given time in a running Java application.
That last point is plainly not true. Indeed, it is demonstrably not true if you have a large enough heap to hold 232 instances of java.lang.Object ... in a 64-bit JVM.
And a third way is to some well-known examples where two different two character strings have the same hashcode.
Given that your assumption is incorrect, the reasoning that follows from it is also incorrect.
Java does need an equals method.
You generally cannot implement equals using just hashCode.
You may be able to use hashCode to implement a faster equals method, but only if calling hashCode twice is faster than comparing two objects. It generally isn't.
hashCodes are equal -> Objects might be equal -> further comparision is required
hashCodes are different -> Object are not equal (if hashCode is implemented right)
That's how equals method are implemented. At first you check if hashCodes are equal. If yes, you need to check class fields to see if it represents the exact same object. If hashCodes are different, you can be sure that objects are not equal.
Sometimes (very often?) you don't!
These answers are not untrue. But they don't tell the whole story.
One example would be where you are creating a load of objects of class SomeClass, and each instance that is created is given a unique ID by incrementing a static variable, nInstanceCount, or some such, in the constructor:
iD = nInstanceCount++;
Your hash function could then be
int hashCode(){
return iD;
}
and your equals could then be
boolean equals( Object obj ){
if( ! ( obj instanceof SomeClass )){
return false;
}
return hashCode() == obj.hashCode();
}
... under such circumstances your idea that "equals is superfluous" is effectively true: if all classes behaved like this, Java 10 (or Java 23) might say, ah, let's just get rid of silly old equals, what's the point? (NB backwards compatibility would then go out the window).
There are two essential points:
you couldn't then create more than MAXINT instances of SomeClass. Or... you could ... if you set up a system for reassigning the IDs of previously destroyed instances. IDs are typically long rather than int ... but this wouldn't work because hashCode() returns int.
none of these objects could then be "equal" to another one, since equality = identity for this particular class, as you have defined it. Often this is desirable. Often it shuts off whole avenues of possibilities...
The necessary implication of your question is, perhaps, what's the use of these two methods which, in a rather annoying way, have to "cooperate"? Frelling, in his/her answer, alluded to the crucial point: hash codes are needed for sorting into "buckets" with classes like HashMap. It's well worth reading up on this: the amount of advanced maths that has gone into designing efficient "bucket" mechanisms for classes like HashMap is quite frightening. After reading up on it you may come to have (like me) a bit of understanding and reverence about how and why you should bother implementing hashCode() with a bit of thought!

Can I implement hashCode and equals this way for (Hash)Sets?

I know that if a.equals(b), we must have a.hashCode() == b.hashCode() else we get strange results, but I'm wondering if the converse is also required.
More specifically, I have a hashCode() function that uses the field id to calculate the hashCode. However, my equals() function only uses the simple comparison "==" to check for equality. This may seem strange but unless more details are required, it's simply how I've implemented it.
Now the question is will this mess anything up? Specifically for HashSets, but more generally, for any (common) implementations of Set.
The way I understand it, a Set will first check the hashCode then the equals operator to see if a duplicate object exists. In this case, it should work right? If two objects are the same instance, they will produce the same hashCode as well as return true for equals() and thus the Set will only allow the instance to be be added once.
For two separate instances with the same id, the hashCode will be identical but then the equals() operator will return false and thus allow both objects to enter the Set, which is what I hope to accomplish.
Is this a beginner's mistake? Am I missing something? Will this have an unexpected results for any collection types other than Set?
edit:
I guess I should explain myself. I have a Hibernate object FooReference which implements both a hashCode and equals method using the id. This object is guaranteed to always have a unique id. However, before this object is persisted, I use a Foo object which has a default id of -1. So when putting it in a Set (to be saved) I know each Foo is unique (thus the basic == operator). So this Foo which extends FooReference overrides the equals method with a basic ==. For my purposes this should work... hopefully.
Objects are allowed to have the same hashcode without being equal to each other. In fact, it's perfectly valid (though inefficient and a bad idea) to implement hashCode as simply return 0, giving every instance the same hashcode.
All that's required is that if two objects are equal (as determined by the equals method), they have the same hashcode.
However, if your equals method just compares the two objects using == internally, no two (distinct) instances will ever be equal to each other, so there's no point defining your own hashCode and equals methods at all. The default implementations will produce the same behavior.

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

Java: If I overwrite the .equals method, can I still test for reference equality with ==?

I have the following situation: I need to sort trees based by height, so I made the Tree's comparable using the height attribute. However, I was also told to overwrite the equals and hashCode methods to avoid unpredictable behaviour.
Still, sometimes I may want to compare the references of the roots or something along those lines using ==. Is that still possible or does the == comparison call the equals method?
equals() is meant to compare an object with rules set by the programmer. In your example you compare your trees by height, so you'll write equals() so it compares heights.
==, as you said, compares references. These aren't touched neither by equals() nor by hashCode(). So you won't change its behaviour.
Yes, == will not call hashCode or equals. You can still test for reference equality like this.
== does not call equals. So it's still find for identity checks.
As many implementations of equals start with this == other check you would get a literal StackOverflow if it were calling equals behind the scenes.
I think that a bigger question here is whether it is appropriate to implement comparable on these objects. It may be more appropriate to use a Comparator for the operations that work on height, and not embed ordinal computation in the class itself.
My general philosophy on this is to only implement Comparable if there is a truly natural ordering for the object. In the case of a tree node, is height the only way that anyone could ever want to sort? Maybe this is a private class, and the answer is 'yes'. But even then, creating a Comparator isn't that much extra work, and it leaves things flexible in case you decide you want to make that tree node a protected or public class some day.
== tests referential equality. It will not call equals.
Overriding the equals() method will have NO effect on the == operator.
== is used to test if 2 references point to the same object.
equals() method "meaningfully" compares 2 objects.
It is important to realize the implication of the work "meaningful" here. Equality is easier to understand when you are comparing, for instance, 2 Strings or 2 integers. This is why, the equals() method - inherited from the Object class - is already overridden by the String and Wrapper classes (Integer, Float, etc). However, what if you are comparing 2 objects of type Song. Here, equality can be established on the basis of
1) Artist name
2) Song name
3) or some other criterion
Therefore, you have to override the equals() method to "explicitly" determine "when" 2 Song objects are considered equal.
The "unpredictable behavior" you mentioned in your question relates to objects like the one above (Song) behave when dealing with Collections like Map. You SHOULD NOT use these objects in a map until you override both the equals() and hashcode() method. The reason being how hashmap search and indexing works. Refer the JavaDoc for the specifc rules. What you should remember is:
If 2 objects are meaningfully equal, their hashcode should return the same value. However, it is not necessary for 2 objects to be equal, if they return the same hashcode. Again, Java doesn't enforce any rules regarding this. It is your responsibility to implement the equals() and hashcode() methods correctly.

Categories

Resources