Why do contains()/indexOf() in Java collections use o.equals(e) and not e.equals(o)?

Why are the methods contains() and indexOf() in the Java collections framework defined using o.equals(e) and not e.equals(o) (where o is the argument of the methods and e is the element in the collection)?

Anyone know the reasons of that?

Answers


Because o is known not be null, but e isn't necessarily. Take this example from the code for LinkedList:

for (Entry e = header.next; e != header; e = e.next) {
    if (o.equals(e.element))
        return index;
    index++;
}

In this example, doing it this way round avoids the need to protect against e.element being null for every item in the collection. Here's the full code that takes account of o being null:

if (o == null) {
    for (Entry e = header.next; e != header; e = e.next) {
        if (e.element == null)
            return index;
        index++;
    }
} else {
    for (Entry e = header.next; e != header; e = e.next) {
        if (o.equals(e.element))
            return index;
        index++;
    }
}

The difference between using x.equals(y) and y.equals(x) is the following:

If x is null, y.equals(x) would simply return false whyle x.equals(y) would result in a NullPointerException.


Assume you have a collection of friendly objects, e.g.

class Friendly {
    final int i;
    Friendly( int i ) { this.i = i; }
    public boolean equals( Object o ) {
        return o != null && o instanceof Friendly && ((Friendly)o).i == this.i;
    }
}

java.util.Collection c = new java.util.ArrayList();
for( int i = 0; i < 10; i++ )
    c.add( new Friendly(i) );

and you want to insert an unfriendly object, which is never to be found in a collection by contains:

class Unfriendly extends Friendly {
    Unfriendly( int i ) { super(i); }
    public boolean equals( Object o ) { return false; }
}

If contains called e.equals(o) then your sinister plan would fail, as the friendly elements already in the collection get to decide whether an unfriendly object equals to them or not. By calling o.equals(e) it is the unfriendly object passed to contains which gets to decide what it wants to be equal to, allowing you to make use of the method overriding facility.

System.out.println("contains friendly? " + c.contains(new Friendly(1)));
#> contains friendly? true
System.out.println("contains unfriendly? " + c.contains(new Unfriendly(1)));
#> contains unfriendly? false

contains friendly? is true because we are using ArrayList; had we been using HashSet or anything hashed, it becomes necessary to specify a hashCode method too.

Notice that although the contract for contains requires the use of the equals method, some implementations may not conform to this, such as org.eclipse.emf.common.util.BasicEList, wasting your time by having you figure that out.

In this case you have to do the comparison yourself, e.g.

boolean contained = false;
for( Object e : c )
    if( o.equals(e) ) { contained = true; break; }

Need Your Help

Connect to oracle database with C++

c++ oracle connection

I am looking for a way to connect to an remote oracle database and read some data from table within a c++ console application.

Best way to implement class library containing standards which changes yearly

architecture class-design

I want to create a class library for some standards. These standards are updated yearly (Not necessarily every year, may take 3-4 years also). I want to maintain older versions also. What is the be...