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
Your equals implementation is wrong. Your in parameter should not be a String
. It should be an InnerClass
.
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof InnerClass) return false;
InnerClass that = (InnerClass)o;
// check for null keys if you need to
return this.testKey.equals(that.testKey);
}
(Note that instanceof null
returns false, so you don't need to check for null first).
You would then test for existence of an equivalent object in your list using:
objectList.contains(new InnerClass("UNIQUE ID1"));
But if you really want to check for InnerClass by String key, why not use Map<String,InnerClass>
instead?
You're invoking contains
with an argument that's a String
and not an InnerClass
:
System.out.println( objectList.contains("UNIQUE ID1"))
In my JDK:
public class ArrayList {
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {
// omitted for brevity - aix
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i])) // <<<<<<<<<<<<<<<<<<<<<<
return i;
}
return -1;
}
}
Note how indexOf
calls o.equals()
. In your case, o
is a String
, so your objectList.contains
will be using String.equals
and not InnerClass.equals
.
in the other way, your equal method gets called if you change your code as follows. hope this clears the concept.
package com.test;
import java.util.ArrayList;
import java.util.List;
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 in1) {
System.out.println("reached here");
if(in1 == null) {
return false;
}else if( in1 instanceof InnerClass) {
return ((InnerClass) this).testKey == null ? false : ((InnerClass) this).testKey.equals(((InnerClass) in1).testKey);
}else {
return false;
}
}
}
public static void main(String[] args) {
ArrayList<InnerClass> objectList = new ArrayList<InnerClass>();
InnerClass in1 = new InnerClass("UNIQUE ID1", 42);
InnerClass in2 = new InnerClass("UNIQUE ID1", 42);
//add some entries
objectList.add(in1);
System.out.println( objectList.contains(in2));
}
}
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.
Although not answering your question, many Collections use hashcode()
. You should override that too to "agree" with equals()
.
Actually, you should always implement both equals
and hashcode
together, and they should always be consistent with each other. As the javadoc for Object.equals()
states:
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.
Specifically, many Collections rely on this contract being upheld - behaviour is undefined otherwise.