Why does equals() in Java must be passed Object as a parameter? - java

When we override the equals() method in Java, I know that Object needs to be a parameter, but I wonder - why Object?.
Second, let us say we override hashcode() and implement equals(), but set the parameter in equals() to MyClass instead of Object (MyClass being the class whose equals() method we override). Will we still get the expected behavior if we use HashMap?
Update: Yes, it will be overloading instead of overriding. But what will happen if we use HashMap with overloaded equals()? Also, I don't find the answer in related posts. Or is it something obvious that I am missing?

If you write an equals() method whose parameter is not Object, you are overloading the method, not overriding it.
Now, as for HashMap - HashMap calls equals to compare keys. The type of the compared keys is Object. Therefore, if you define an equals() method with a parameter whose not Object, this method will be ignored by HashMap.
I tried the following code :
public class SomeClass
{
int privateMember;
// note it's important to override hashCode, since if the hashCode of two
// keys is not the same, equals() won't be called at all
public int hashCode ()
{
return privateMember;
}
public boolean equals (Object other)
{
if (other instanceof SomeClass) {
return this.privateMember==((SomeClass)other).privateMember;
}
else {
return false;
}
}
public static void main(String[] args)
{
HashMap<SomeClass,String> map = new HashMap<SomeClass,String>();
SomeClass s1 = new SomeClass ();
SomeClass s2 = new SomeClass ();
s1.priv=4;
s2.priv=4;
map.put (s1, "something");
if (map.containsKey (s2)) {
System.out.println ("found!");
} else {
System.out.println ("not found!");
}
}
}
This code outputs "found!".
Now, if you run the exact same code, but replace the equals method with :
public boolean equals (SomeClass other)
{
if (other instanceof SomeClass) {
return this.privateMember==((SomeClass)other).privateMember;
}
else {
return false;
}
}
The output will be "not found!", which means our equals method was ignored.

The collections use the equals and hashcode methods from the Object base class. Therefore you must override them in order for your custom class to provide an implementation. You can overload equals if you wish, and that would work for situations where some code knows that it's dealing with an instance of MyClass. However, this would be misleading.
All the collections classes are designed to work with instances of Object and Object provides a general purpose equals method.
You shouldn't really need to write an equals method directly. You can either generate one using your IDE, or use EqualsBuilder from Apache Commons (https://commons.apache.org/proper/commons-lang/javadocs/api-3.1/org/apache/commons/lang3/builder/EqualsBuilder.html) to help put it all together.

Related

Is overriding hashCode and equals a necessity when adding non-primitive objects to a TreeSet?

I'm following a tutorial to better understand Natural Ordering, using TreeSet and the Comparable interface.
The tutorial tells me that, to add non-primitive custom objects to Sets, I need to implement equals() and hashCode(). However, even without implementing these methods I'm able to compile and run the code (as below). I am using IntelliJ with Java 8.
Is overriding equals() and hashCode() absolutely necessary when working with TreeSets (SortedSet interface) and natural ordering?
class My_Person implements Comparable<My_Person>{
private String name;
public My_Person(String name) {
this.name = name;
}
public String toString() {
return name;
}
// #Override
// public boolean equals(Object o) {
// if (this == o)
// return true;
// if (o == null || getClass() != o.getClass())
// return false;
// My_Person my_person = (My_Person) o;
// return Objects.equals(name, my_person.name);
// }
//
// #Override
// public int hashCode() {
// return Objects.hash(name);
// }
#Override
public int compareTo(My_Person person) {
return name.compareTo(person.name);
}
}
public class NaturalOrdering {
public static void main(String[] args) {
List<My_Person> list = new ArrayList<>();
Set<My_Person> set = new TreeSet<>();
addElement(list);
addElement(set);
Collections.sort(list);
showElement(list);
System.out.println("\n");
showElement(set);
}
private static void addElement(Collection<My_Person> collection) {
collection.add(new My_Person("Joe"));
collection.add(new My_Person("Sue"));
collection.add(new My_Person("Juliet"));
collection.add(new My_Person("Clare"));
collection.add(new My_Person("Mike"));
}
private static void showElement(Collection<My_Person> collection) {
for(My_Person element: collection) {
System.out.println(element);
}
}
}
This depends on your requirements for equality. If you don't override equals and hashCode, two objects are defined as equal if and only if they are identical (i.e. the same object). If you need some other definition for equality you must override the methods.
It is not "absolutely necessary", but then you may get unexpected/wrong output if you don't do so. So if you don't override them, it may still work sometimes, but it may fail too. So just to be safe, better override it.
It will fail if you need to check equality. But if you are only concerned with sorting of the stored objects as per your own defined logic in compareTo method, then I think there is no need to override equals or hashcode.
Its not required by TreeSet or for that matter any Set. But Set is by nature collection of unique objects. For primitive types, Java has way to know whether two objects are same or not. For non-primitive user has to tell Java on how to know whether two objects are same or not and way is to override equals and hashcode methods which are invoked by Set#add method to determine the object being added already exists in the set or not. So if you need a functional unique objects in your Set, you should implement equals and hashcode methods. Hope this helps. This is a good read on the same topic.
You have the source code for TreeSet (and TreeMap which TreeSet uses under the hood). You can clearly see that TreeSet (and TreeMap) rely on compareTo() and not hashCode().
On the other hand, the HashSet (and HashMap which HashSet uses under the hood) does use hashCode() and equals(). No big surprise, considering that they are named HashSet and HashMap.

Can't Override Equals method on Java when calling contains

I have the following funtion that checks if a List of codigos contains a single codigos object:
if (!concorrente.getJcodigoses().contains(cod))
{
return "redirect:" + referrer;
}
I read that i need to Override the equals method like so:
#Override
public boolean equals(Object object)
{
boolean isEqual= false;
if (object != null && object instanceof Jcodigos)
{
isEqual = (this.id == ((Jcodigos) object).id);
}
return isEqual;
}
I placed it in my Jcodigos.java class and i noticed that concorrente.getJcodigoses().contains(... never gets into my custom equals method...
Any advice?
Thanks
Answer:
I was missing the following method
#Override
public int hashCode() {
return this.id;
}
You need to provide an complmentary hashCode() method whenever providing an equals() method and visa-versa.
The reason for this is to fulfil the API contracts when interacting with the objects in collections. See the site for tips on creating the code hashcode-equals
The Java Docs have more information about the requirements. As stated in the documentation about the equals method:
Note that it is generally necessary to override the hashCode method
whenever this method is overridden, so as to maintain the general
contract for the hashCode method, which states that equal objects must
have equal hash codes.
You have to implement hashCode as well.

Comparing two comparator objects in Java

If I do the following
myObject.myMethod(myClass.getComparator());
with
public void myMethod(Comparator<? super myOtherObject> comparator) {
if (comparator.equals(myClass.getComparator()) {
//do sth
}
}
and in myClass
static Comparator<ListItem> getComparator() {
return new Comparator<myOtherObject>() {
public int compare(myOtherObjectitem1, myOtherObjectitem2) {
return (Integer.valueOf(myOtherObject.getRating()).compareTo(Integer.valueOf(myOtherObject.getRating())));
}
};
}
then "//do sth" is not gonna be executed. So the objects I get from getComparator the two times are different. How can that be? Is there a chance to see, which comparator "myMethod" gets?
You're calling the equals method on this line:
if (comparator.equals(myClass.getComparator())
Since you haven't defined this method explicitly on your Comparator class (which is an anonymous inner class), this defaults to the version inherited from Object - which considers two references equal only if they are the exact same object.
And your getComparator() method states return new Comparator() { ... }, so it's calling the constructor and creating a new object each time it's called. Thus the result of one call to getComparator will be a distinct object, and hence will not be considered equal to, the result of another call.
I can think of two possible ways to change your code so that the equality test returns true:
Create the comparator only once, and return this same object from
getComparator. This would involve a change somewhat like the
following in myClass:
private static Comparator<ListItem> cmp = new Comparator<myOtherObject>() {
public int compare(myOtherObjectitem1, myOtherObjectitem2) {
return (Integer.valueOf(myOtherObject.getRating()).compareTo(Integer.valueOf(myOtherObject.getRating())));
}
};
static Comparator<ListItem> getComparator() {
return cmp;
}
Provide an explicit equals() implementation (and thus a hashCode() one too, ideally). You can then control exactly which objects are considered equal to one of your comparators. This might be much easier if you define a concrete class for your comparator rather than it being an anonymous inner class.
At the end of the day, though, I fear your approach might not be right. What does it mean for two comparators to be equal to one another? I feel this is an ambiguous concept for anything other than data classes, and I would be hesitant to use the Object.equals method for this.
(For example, if by equality you mean "they will sort lists in the same order", then I'd add a method to your comparator class called isEquivalentSortOrder or something similar. This way you can specify exactly what you mean without having to rely on the woolly definition of "being the same".)
Why not to create inside myClass static variable of Comparator like:
class myClass{
public static Comparator<ListItem> = new Comparator<myOtherObject>() {
public int compare(myOtherObjectitem1, myOtherObjectitem2) {
...
}
};
}

Is it OK to override a Set's contains() method?

I was playing around with Java reflection, and I wanted to create a method caching mechanism from Methods declared in different classes. In order to prevent random behavior, I want to forbid loading methods with the same signature to the Cache (method declared in different classes can have the same signature).
The only way I found of doing this, was to Override the contains() method of the Set where I cache the methods.
Is it dangerous to do so? Do you have any better idea to achieve this?
private final Set<Method> methodsCache;
public MyMethodCachingClass(Set<Class<?>> classes) {
methodsCache = new HashSet<Method>(){
private static final long serialVersionUID = -1467698582662452923L;
/**
* Overwriting the contains method of this Set so that we don't allow multiple methods with the same signature,
* even if they are declared in different classes.
*/
#Override
public boolean contains(Object o) {
if (!(o instanceof Method)) {
return false;
}
Method method = (Method) o;
for (Method m : this) {
if (method.getName().equals(m.getName()) && method.getParameterTypes().equals(m.getParameterTypes())) {
return true;
}
}
return false;
}
};
for (Class<?> c : classes) {
for (Method m : c.getDeclaredMethods()) {
if (methodsCache.contains(m)) {
throw new IllegalArgumentException("The method " + m.getName() + " exists with the same signature in two different classes.");
}
methodsCache.add(m);
}
}
}
Thanks!
Simply use a combination of the following for your cache key:
class name + method name + method parameter types
It is entirely okay to override the "contains()" method - however, it is generally unnecessary. The purpose of the contains() method is simply to check wether an equivalent object already exists in the collection.
The "equals" method for your particular object collection will be used to ascertain this.
However, if you want custom behavior for contains which CANNOT be embedded in the object, it might be worthwhile to hack the contains() method. I think , given the fact that you are trying to cache java Methods, you might want to glue the "contains" logic into the set containing these methods.... However, its not entirely clear.
I agree with the above post (#jayunit100). However, I wouldn't override contains() for this. Instead, I'd write an implementation of Comparator and use a SortedSet. Eg:
SortedSet<Method> cachedMethods = new TreeSet<Method>(new Comparator<Method>() {
// compare() implementation omitted
});

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