7 Replies Latest reply: Apr 18, 2013 7:32 AM by gimbal2 RSS

    equals method and contains

    csh
      Hi,

      I've overridden the equals method of an object and put it into an ArrayList.

      When I call list.contains("string"), I would have expected that each element of the ArrayList calls its equals method (my overriden one) and passes "string".

      But instead the equals method is called on "string", which gets passed my object.

      Is that a bug or is that by design?

      import java.util.ArrayList;
      import java.util.List;
      
      public class TestApp {
      
          public static void main(String[] args) {
      
      
              List<MyObject> myList = new ArrayList<MyObject>();
      
              myList.add(new MyObject("test"));
      
              System.out.println(myList.contains("test")); // Returns false, but expected true.
          }
      
          private static class MyObject {
      
              private String s;
      
              private MyObject(String s) {
                  this.s = s;
              }
      
              @Override
              public boolean equals(Object o) {
                  if (o != null && o instanceof MyObject) {
                      return ((MyObject) o).s.equals(s);
                  }
                  return false;
              }
      
              @Override
              public int hashCode() {
                  return s.hashCode();
              }
          }
      From Java API:
      public int indexOf(Object o) {
              if (o == null) {
                  for (int i = 0; i < size; i++)
                      if (elementData==null)
      return i;
      } else {
      for (int i = 0; i < size; i++)
      if (o.equals(elementData[i])) // THIS SHOULD BE THE OTHER WAY ROUND.
      return i;
      }
      return -1;
      }
      Edited by: csh on 16.04.2013 02:21                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
        • 1. Re: equals method and contains
          gimbal2
          Check out your equals. It does not expect a String - it expects a MyObject. And rightfully so, a String object should never be equal to a MyObject object.

          So I would expect you to do this:
          System.out.println(myList.contains(new MyObject("test")));
          • 2. Re: equals method and contains
            csh
            Ok, and what about this?:
            @Override
                    public boolean equals(Object o) {
                        if (o != null && (o instanceof MyObject || o instanceof String)) {
                            if (o instanceof String) {
                                return s.equals(o);
                            }
                            return ((MyObject) o).s.equals(s);
                        }
            
                        return false;
                    }
            • 3. Re: equals method and contains
              Kayaman
              Nope. That still allows MyObject to equal to String. That breaks the contract for the equals() method.

              If o is not instanceof MyObject, you must return false.
              • 4. Re: equals method and contains
                jtahlborn
                Kayaman wrote:
                Nope. That still allows MyObject to equal to String. That breaks the contract for the equals() method.

                If o is not instanceof MyObject, you must return false.
                that's not strictly true, but it generally holds. the more important rule is that equals must be commutative, i.e. if "a.equals(b)" then "b.equals(a)". in this case "String.equals(MyObject)" will never be true, so this is a bad implementation of equals.
                • 5. Re: equals method and contains
                  1003353
                  sometimes exists a == b, but b != a, for example:
                  Map<String, String> map1 = new IdentityHashMap<String, String>();
                  map1.put("1", new String("2"));
                  Map<String, String> map2 = new HashMap<String, String>();
                  map2.put("1", "2");
                  System.out.println(map1.equals(map2));   // false
                  System.out.println(map2.equals(map1));   // true
                  This isn't a bug, because the comment in IdentityHashMap
                  This class implements the <tt>Map</tt> interface with a hash table, using
                  reference-equality in place of object-equality when comparing keys (and
                  values).
                  • 6. Re: equals method and contains
                    jtahlborn
                    1000350 wrote:
                    sometimes exists a == b, but b != a, for example:
                    i didn't say it didn't exist. that still technically violates the contract for equals. whether or not that was a good idea is up to the designers of IdentityHashMap.
                    • 7. Re: equals method and contains
                      gimbal2
                      1000350 wrote:
                      sometimes exists a == b, but b != a, for example:
                      Map<String, String> map1 = new IdentityHashMap<String, String>();
                      map1.put("1", new String("2"));
                      Map<String, String> map2 = new HashMap<String, String>();
                      map2.put("1", "2");
                      System.out.println(map1.equals(map2));   // false
                      System.out.println(map2.equals(map1));   // true
                      This isn't a bug, because the comment in IdentityHashMap
                      This class implements the <tt>Map</tt> interface with a hash table, using
                      reference-equality in place of object-equality when comparing keys (and
                      values).
                      Nice find. I wonder if another example can be found though, this seems like a rare exception to the rule where breaking the equals contract was only done because there was no other choice. I would -not- use this to justify breaking it in other classes because it is convenient.