Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ArrayList not using the overridden equals

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

like image 724
K.Barad Avatar asked Jul 06 '11 13:07

K.Barad


People also ask

What happens if we do not override equals?

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 .

What happens if we do not override equals in Java?

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).

Does ArrayList have equals method?

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.

Does ArrayList contain equals in Java?

It uses equals, not ==.


2 Answers

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.

like image 137
Sergey Aslanov Avatar answered Sep 24 '22 01:09

Sergey Aslanov


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 and y, x.equals(y) should return true if and only if y.equals(x) returns true.

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.

like image 31
Joachim Sauer Avatar answered Sep 21 '22 01:09

Joachim Sauer