I'm having a problem with getting an ArrayList to correctly use an overriden equals. the problem is that I'm trying to use the equals to only test for a single key field, and using ArrayList.contains() to test for the existence of an object with the correct field. Here is an example
public class TestClass {
private static class InnerClass{
private final String testKey;
//data and such
InnerClass(String testKey, int dataStuff) {
this.testKey =testKey;
//etc
}
@Override
public boolean equals (Object in) {
System.out.println("reached here");
if(in == null) {
return false;
}else if( in instanceof String) {
String inString = (String) in;
return testKey == null ? false : testKey.equals(inString);
}else {
return false;
}
}
}
public static void main(String[] args) {
ArrayList<InnerClass> objectList = new ArrayList<InnerClass>();
//add some entries
objectList.add(new InnerClass("UNIQUE ID1", 42));
System.out.println( objectList.contains("UNIQUE ID1"));
}
}
What worries me is that not only am I getting false on the output, but I'm also not getting the "reached here" output.
Does anyone have any ideas why this override is being completely ignored? Is there some subtlety with overrides and inner classes I don't know of?
Edit: Having problems with the site so I cant seem to mark the answered. Thanks for the quick response: yes an oversight on my part that it is the String .equals thta is called, not my custom one. I guess it's old fashioned checks for now
We know that two objects are considered equal only if their references point to the same object, and unless we override equals and hashCode methods, the class object will not behave properly on hash-based collections like HashMap , HashSet , and Hashtable .
As a side note, when we override equals(), it is recommended to also override the hashCode() method. If we don't do so, equal objects may get different hash-values; and hash based collections, including HashMap, HashSet, and Hashtable do not work properly (see this for more details).
The ArrayList. equals() is the method used for comparing two Array List. It compares the Array lists as, both Array lists should have the same size, and all corresponding pairs of elements in the two Array lists are equal.
It uses equals, not ==.
If you check sources of ArrayList
, you will see that it calls equals
of other object. In your case it will call equals
of String "UNIQUE ID1"
which will check that other object is not of type String
and just returns false
:
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
...
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
...
return -1;
}
For your case call contains
with InnerClass
that only contains id
:
objectList.contains(new InnerClass("UNIQUE ID1"))
Don't forget to implement equals
for InnerClass
which compares id
only.
According to the JavaDoc of List.contains(o)
, it is defined to return true
if and only if this list contains at least one element
e
such that(o==null ? e==null : o.equals(e))
.
Note that this definition calls equals
on o
, which is the parameter and not the element that is in the List
.
Therefore String.equals()
will be called and not InnerClass.equals()
.
Also note that the contract for Object.equals()
states that
It is symmetric: for any non-null reference values
x
andy
,x.equals(y)
should returntrue
if and only ify.equals(x)
returnstrue
.
But you violate this constraint, since new TestClass("foo", 1).equals("foo")
returns true
but "foo".equals(new TestClass("foo", 1))
will always return false
.
Unfortunately this means that your use case (a custom class that can be equal to another standard class) can not be implemented in a completely conforming way.
If you still want to do something like this, you'll have to read the specification (and sometimes the implementation) of all your collection classes very carefully and check for pitfalls such as this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With