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)
an开发者_如何学God 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?
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; }
精彩评论