I have some doubts regarding equals and hashcode. What I have understood previously is, we need to override the hashcode() and equals() method in the object class if that class is supposed to add to the collection class or Map class. Please see the below example. I did not override the hashcode and equals method. Still I get the desired result what I want. One thing i understood that, If I want to compare two object we need to override the equals method. But in this example I am not comparing two objects, rather I am adding objects to collection or Map without overriding the hashcode and equals. Could anyone explain why do we need to override the hashcode and when?
package equalshashcode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class EqualsAndHashCode {
public static void main(String[] args) {
Student student1 = new Student("A");
Student student2 = new Student("B");
List<Student> studentList = new ArrayList<Student>();
Map<Integer,Student> studentMap = new HashMap<Integer,Student>();
studentMap.put(1, student1);
studentMap.put(2, student2);
studentList.add(student1);
studentList.add(student2);
System.out.println("Student HashMap:: "+studentMap.get(1));
System.out.println("Before removing::"+studentList);
System.out.println(studentList.remove(student1));//true success
System.out.println("After removing::"+studentList);
}
}
class Student{
String name;
public Student(String pName){
this.name = pName ;
}
public String getName(){
return this.name ;
}
}
You don't always need to implement equals() and hashCode() to add elements into a collection.
For a List (such as you studentList, which is an ArrayList instance) it doesn't matter for most actions.
The only places where you need equals() and hashCode() is where "equality" of objects is relevant. And even in those places the default implementation might even be sufficient.
Equality matters in several cases, for example:
A Set can't contain two objects that are equal to each other
The key of a Map must have a correct equals() implementation to be retrievable (and based on the concrete Map implementation, that might imply that hashCode() has to be correctly implemented as well).
Some methods on a "normal" List also need equality. For example contains(), indexOf() and remove(Object) need correctly implemented equals()/hashCode()
Custom equals and hashCode methods are generally used for determining actual equality. If you rely on the default equals method, this will look for the same instance in memory. For example customObj1.equals(customObj2) will return false if there's no custom equals method and the two objects are separate (but otherwise equal) instances. The idea of a custom equals method is to make that same example return true if they are actually equal. A Map will then be able to use this to match the key object passed in a get(key) request to find the value without needing to have the correct instance of the key (a HashMap actually uses hashCode to do this).
The most obvious example of this being useful is for Strings, so that you can do myMap.get("key") and get the correct value, even if your key instance isn't the same as the one stored in the Map (String implements equals and hashCode for you). In general I would recommend using these Java built-in equals and hashCode methods to construct your own. For example if your object has a natural ID, such as say userName, your equals can use the String version of this to complete your own equals (you can obviously compare multiple fields):
public boolean equals(Object o) {
if (o instanceof MyObj) {
return userName.equals(((MyObj) o).userName));
}
return false;
}
As such, if you'll only be working with a single Thread, or otherwise limited variables, you'll likely never need to implement these methods, but there are certainly times when it pays off to know them.
Collection classes will work perfectly well with objects that have the default equals/hashCode implementations - there is no need to add your own for this reason.
Custom equals/hashCode methods are easy to get wrong, and unless you have a good reason to roll your own you should just stick with the ones that you get for free with java.lang.Object.
The most common reason for creating your own version of these methods is if you want to treat two separate instances of the same class as being equal. For example, if you have an Account class and you want two instances of Account which have the same accountNumber property to be regarded as equal, because they represent the same real-world account. In this case you would base your equals method on the value of accountNumber alone, and since you now have a custom equals method you need to provide a suitable implementation of hashCode as well, in order to fulfil the equals/hashCode contract.
Related
I am facing a bizarre outcome in a java application (Spring batch job) after one of the internal custom dependencies -a library we have developed in my company- has been upgraded.
After the upgrade in the code two new different objects of the same type show to have the same hash code.
CustomObject oj1 = new CustomObject();
oj1.setId(1234L);
CustomObject oj2 = new CustomObject();
oj2.setId(9999L);
System.out.println(oj1); //Prints CustomObject#1
System.out.println(oj2); //Prints CustomObject#1
System.out.println(oj1.hashCode()); //Prints 1
System.out.println(oj2.hashCode()); //Prints 1
I noticed this issue after realizing that one of the unit test which has a HashSet variable was only adding the very first object and ignoring the rests. Obviously the hashSet is doing what is supposed to do but the objects are not supposed to be the same and are new instances with different Ids. I tested the same thing outside of the unit test within the application and still the same issue. As soon as I revert back to the old dependency code behaves normally and the above print statements show different numbers!
I am sure one of the dependencies is causing this issue abut I am not able to determine the root cause.
CustomObject is being pulled indirectly through that same dependency and does not have equals() and hashcode() implemented, it only has
private static final long serialVersionUID = 1L;
Looking at the source of CustomObject reveals this implementation
public class CustomObject extends BaseModel implements Serializable
and BaseModel has the equals and hashCode methods defined
import org.jvnet.jaxb2_commons.lang.*;
import org.jvnet.jaxb2_commons.locator.ObjectLocator;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
import java.io.Serializable;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "BaseModel")
#XmlSeeAlso({
CustomObject.class
})
public abstract class BaseModel implements Serializable, Equals2, HashCode2
{
private final static long serialVersionUID = 1L;
public boolean equals(ObjectLocator thisLocator, ObjectLocator thatLocator, Object object, EqualsStrategy2 strategy) {
if ((object == null)||(this.getClass()!= object.getClass())) {
return false;
}
if (this == object) {
return true;
}
return true;
}
public boolean equals(Object object) {
final EqualsStrategy2 strategy = JAXBEqualsStrategy.INSTANCE;
return equals(null, null, object, strategy);
}
public int hashCode(ObjectLocator locator, HashCodeStrategy2 strategy) {
int currentHashCode = 1;
return currentHashCode;
}
public int hashCode() {
final HashCodeStrategy2 strategy = JAXBHashCodeStrategy.INSTANCE;
return this.hashCode(null, strategy);
}
}
Thank you in advance.
Clearly something has changed in a base class, and you will just have to find it and fix it, or else implement hashCode() and equals() acceptably in this class.
Somebody somewhere has implemented hashCode() to return 1, which is idiotic. They would have been better off not to implement it at all. And it's not hard to find. Just look at the Javadoc for CustomObject and see where it inherits hashCode() from.
I think you have already realized what the answer to your question is, but the code that you added in your update makes it clear:
Your CustomClass extends the BaseClass.
The BaseClass overrides Object::hashCode()
The override in the version of BaseClass that you showed us will always return 1. It is calling a hashCode(ObjectLocator, HashCodeStrategy2) method with a specific strategy, but the implementation of that method simply ignores the strategy argument.
Now is pretty clear that that version the BaseClass code can only ever return 1 as the hashcode. But you say that your code used to work, and you only changed the dependency. From that, we must conclude that the dependency has changed, and that the new version of the dependency is broken.
If anything is "bizarre" about this, it is that someone decided to implement the (new) BaseClass like that, and release it without testing it properly.
Actually, there is a possible way that you can get your CustomClass to work. The BaseClass::hashCode(ObjectLocator, HashCodeStrategy2) method is public and not final, so you could override it on your CustomClass as follows:.
#Override
public int hashCode(ObjectLocator locator, HashCodeStrategy2 strategy) {
return System.identityHashCode(this);
}
And indeed, it may be that the implementers of BaseClass intend you to do this. But I would still argue that BaseClass is broken:
Coding the class so that hashCode returns 1 as the default behavior is nasty.
There is no javadoc in BaseClass to explain the need to override the method.
Their (unannounced?) change to the behavior of BaseClass is an API breaking change. You shouldn't do stuff like that, without very good reason, and without warning.
The default behavior of the corresponding equals method is objectively wrong.
It's fine for two non-equal objects to have the same hash code. In fact, it's a mathematical requirement that this be allowed. Think about strings, for instance: there are infinitely many non-equal strings ("a", "aa", "aaa"...) yet only 2^32 possible int values. Clearly there must be different strings that share a hash code.
But HashSet knows this, so it uses the result from equals as well as the hash code. If only one of the objects is being added, then they don't just have there same hash code -- they are equal, as returned by the equals method. We can't determine why this is, let alone whether it's intentional, without the custom class's code.
The contract for Object says that equal objects must have the same hash code. But the converse is not true: objects with the same hash code do not have to be equal. The Javadocs say this explicitly:
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
Unless a class's docs explicitly tell you how it computes its hash code, you should probably not consider that an established contact, and you should expect that it could change between versions.
CustomObject class implementation (or one of its ancestors) is the problem here. Author of CustomObject (or one of its ancestors) has overridden toString, hashCode and equals methods in a wrong way without understanding the semantics of it and the implications of it. Here are your options (not necessarily in that order) to fix the problem:
You should notify the author of your dependency library about the problem in CustomObject class and get toString, hashCode and equals methods implemented or overridden in the right way. But remember - dependency code author can put you back in this place again in future.
Assuming CustomObject class is not final - extend CustomObject (please note - it is better named as CustomClass, not CustomObject) to have right implementation of toString, hashCode and equals methods. Use this extended class in your code instead of CustomObject class. This will give you better control because your dependency code cannot put you in this trouble again.
Use AOP to introduce overridden and right implementation of toString, hashCode and equals methods in CustomObject class. This approach is also future proof like the option 2 above.
I read javadoc of this JAVA API method, System.identityHashCode(Object x) and not able to understand a typical use case of this method. Classes that need hashCode() are recommended to have overridden hashCode() method of their own so what is the purpose of this method if Object class already has default hashCode()?
Suppose that class C extends class B and class B overrides hashCode and equals.
Now suppose that for class C, you wish to use the default implementation of hashCode and equals as implemented in the Object class. Normally you don't want to do that, but suppose that each instance of the C class should be a unique key in some HashMap.
You can write :
public class C extends B
{
#Override
public int hashCode ()
{
return System.identityHashCode(this);
}
#Override
public boolean equals (Object other)
{
return this == other;
}
}
Similarly, if B override's toString and you want C's toString to have the default implementation of the Object class, you can write in C :
#Override
public String toString()
{
return getClass().getName() + "#" + Integer.toHexString(System.identityHashCode(this));
}
There aren't a lot of use cases. It's primarily useful if for some reason you have a collection of objects, and only care about their object identities. This is really rare. One example is IdentityHashMap (like #Boris says). It would actually be fine to just use hashCode of the objects for such hash map, but using identity hash would theoretically be faster, because it can avoid "collisions" between objects that are logically equal but aren't the same object (it also allows avoiding of badly implemented hash functions, I guess).
There aren't a lot of use case for such collections, either. You can see use cases for IdentityHashMap here: Practical use of IdentityHashMap in Java 6
Just one difference, if the Object given is null this Method gives 0.
Example 1:
obj.hasCode();
This code throws nullPointerException if the obj is null.
Example 2:
System.identityHashCode(obj);
This method return 0 and doesn't throw an exception cause it checks for null. Also it gives the HashCode that the default hashCode method would return even if you overrides it
Did this answer help you?
EDIT: the question is why cant I use ".contains" (override .equals() in the object) to compare object attributes (a string) instead of comparing the object itself.
Thanks JB:
Turns out I was confusing overriding compareTo() with overriding equals()
EDIT:
QUESTION REDEFINED:
Why cant I override equals to compare strings in my object:
public boolean equals(Object obj){
...
if(obj instanceof String){
String testString = (String) obj;
...
}
...
}
Or even overload for that matter:
public boolean equals(String stringObj){
...
}
I read somewhere that the compiler doesn't use logic to decide this, it uses types. So if I then call myObj.equals(stringOne + "_" + stringTwo) shouldn't this work as it knows a string is being passed?
Thanks,
Steve.
Why this code doesn't make sense:
because Comparable is supposed to respect rules: if a < b is true, then b > a must be true. You can compare an instance of your class with String, but a String can't compare with an instance of your custom class. You're thus breaking the contract of Comparable
because Vector doesn't use compareTo() to check if an element exists or not. It uses equals().
Because you shouldn't alter the nature of a class just to implement a specific use-case consisting in adding instances of this class to a list and check for duplicates. This logic should be outside of the class. You could simply loop through the list and check is an instance with the item and position already exists or not, before creating your custom class instance.
because you shouldn't use Vector for many years (since Java 2 - we're at Java 8). And since it seems the goal is to avoid duplicates, what you should use is a Set.
Do the right thing, and use a HashSet. Make sure CustomClass implements equals() and hashCode() correctly. You could also use a HashMap<CustomClassKey, CustomClass>, where CustomClassKey is a simple class containing the two fields identifying your CustomClass instances.
So this is from Head First Java (page 563)
The default behaviour of hashCode() is to generate a unique integer
for each object on the heap. So if you don’t override hashCode() in a
class, no two objects of that type can EVER be considered equal.
But a simple Test will disprove this I think.
public class Song {
private String name;
public Song(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public boolean equals(Object obj) {
Song objectSong = (Song) obj;
return this.getName().equals(objectSong.getName());
}
}
Well this will return true:
Song songA = new Song("A","B");
Song songB = new Song("A","C");
System.out.println(songA.equals(songB));
Am I missing something? What is it that the book is trying to tell me?
The default behaviour of hashCode() is to generate a unique integer
for each object on the heap. So if you don’t override hashCode() in a
class, no two objects of that type can EVER be considered equal.
You're misinterpreting. The author isn't saying that a method (that's all equals and hashCode are) can't return true for two different objects. They're saying that semantically two objects should not be considered equal if the result of their hashCode isn't equal. They are describing how hashCode should be used. If you use it differently, you may get weird and unexpected results.
You need to override hashcode so that the class behaves as expected with hashcode based collections (and anything else).
For example what happens if you put your song in a HashSet, and then try to put another one in ?
You should always override Hashcode if you override equals, and vice versa. Make sure they are consistent.
(josh bloch effective java, is a good starting point)
In Java, every object has access to the equals() method because it is
inherited from the Object class. However, this default implementation
just simply compares the memory addresses of the objects. You can
override the default implementation of the equals() method defined in
java.lang.Object. If you override the equals(), you MUST also override
hashCode(). Otherwise a violation of the general contract for
Object.hashCode will occur, which can have unexpected repercussions
when your class is in conjunction with all hash-based collections.
Source: http://www.xyzws.com/javafaq/why-always-override-hashcode-if-overriding-equals/20 (take a look at it, it has good examples).
Conclusion: you only need to override equals for a simple comparison. However, you SHOULD also override hashCode for other aspects, such as hash based collections.
Java provides way to define comparison of object outside scope Object using Comparator.
Now my questions is why java does not allow do same for equals() and hashcode().
Now each collection contains() method can easily use this external equality provider to check objects are equal.
Guava has the Equivalence class, which does pretty much what you are asking for.
You can even wrap an Object in an Equivalence to decorate an Object with a better hashCode() equals() implementation (e.g. if you want to use an Object with a bad equals() hashCode() as a Map key but don't have access to the sources)
Here's an example: arrays don't have proper implementations of equals() and hashCode(), but here's an Equivalence for char arrays:
private static final Equivalence<char[]> CHAR_ARRAY_EQUIV = new Equivalence<char[]>(){
#Override
protected boolean doEquivalent(char[] a, char[] b) {
return Arrays.equals(a, b);
}
#Override
protected int doHash(char[] chars) {
return Arrays.hashCode(chars);
}
};
Sample code:
final char[] first ={'a','b'};
final char[] second ={'a','b'};
Assert.assertFalse(first.equals(second));
Assert.assertFalse(first.hashCode() == second.hashCode());
final Wrapper<char[]> firstWrapped = CHAR_ARRAY_EQUIV.wrap(first);
final Wrapper<char[]> secondWrapped = CHAR_ARRAY_EQUIV.wrap(second);
Assert.assertTrue(firstWrapped.equals(secondWrapped));
Assert.assertTrue(firstWrapped.hashCode() == secondWrapped.hashCode());
Comparator interface is used to compare objects while sorting collections, and its compare() method returns an "int", means, comparision ends with an int value, which can be used to indicate object's place in a sorted collection.
contains() calls equals() method for each instance in a collection in order to find out whether two instances are equal according to equals- contract.
equals and hashCode are concepts that don't change for a given Object. Only the implementor knows what values should be used according to the rules for these methods. Once he decided about those they define the identy of the object and thus should not be changed ever.
Comparasion on the other hand can be highly dependent on the context. You can define a "natural" order by implementing Comparable. But this can't change for different contexts. Say you have a list of Contacts that can be sorted by last name, first name, zip code, city... You can easily do this by providing separate Comparators (or a parametrized Comparator). But it is not inherent to the object itself so it should be a class of its own (it could be implemented as static inner class, depending on your code conventions).