Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ArrayList contains method not work as I would expect? [duplicate]

Tags:

java

arraylist

ArrayList use equals() in its contains method to see if provide object equals any item in the list as the document say:

Returns true if this list contains the specified element. More formally, returns true if and only if this list contains at least one element e such that (o==null ? e==null : o.equals(e)). see this

I have this class

class Foo
{
  private String value;
  public Foo(String value)
  {
    this.value = value;
  }

  @Override
  public boolean equals(Object o)
  {
    return o == null ? this.value == null : o.toString().equals(this.value);
  }
}

I want to use contains method to check if item exists like this

List<Foo> list = new ArrayList<Foo>();

Foo item1 = new Foo("item1");
Foo item2 = new Foo("item2");
list.add(item1);
list.add(item2);

System.out.println(item1.equals("item1"));       //return true
System.out.println(list.contains("item1"));      //false !! why?!

but contains method return false , while item1.equals("item1") return true.

why contains return false when it use equals method for provided object

like image 624
Ali Faris Avatar asked Nov 27 '22 07:11

Ali Faris


2 Answers

Your equals() implementation violates the symmetric principle :

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.

item1.equals("item1") returns true

while

"item1".equals(item1) returns false.

So you should not expect that Collection method such as contains() works in a consistent way.

As a general rule, equals() overriding should not try to interoperate with other classes but only with instances of the underlying class.

In your case, it works only for String parameter passed to the method.
You should rather make it working for Foo instances parameter:

  @Override
  public boolean equals(Object o) {
     if (!(o instanceof Foo){ 
       return false;
     }
     Foo other = (Foo) o;
     return Objects.equals(other.value, this.value);
  }
like image 97
davidxxx Avatar answered Dec 04 '22 22:12

davidxxx


Your problem is that your equality is not symmetric. Foo == String does not imply String == Foo.

If you look at the implementation of ArrayList.contains you'll see that it calls objectToFind.equals(objectInList), which may be contrary to what you were expecting:

o.equals(elementData[i])

So in your case that's String.equals(Foo). Because String.equals will return false for anything that's not a String, ArrayList.contains returns false.

like image 31
Michael Avatar answered Dec 04 '22 21:12

Michael