简体   繁体   中英

Why is the comparator used instead of the equals() in Collections?

There is a java bean Car that might contains two values: model and price.

Now suppose I override equals() and hashcode() checking only for model in that way:

public boolean equals(Object o) {            
    return this.model.equals(o.model);
}


public int hashCode() {
    return model.hashCode();
}

This permit me to check if an arraylist already contain an item Car of the same model (and doesn't matter the price), in that way:

List<Car> car = new ArrayList<Car>();


car.add(new Car("carA",100f));
car.add(new Car("carB",101f));
car.add(new Car("carC",110f));

System.out.println(a.contains(new Car("carB",111f)));

It returns TRUE. That's fine, because the car already exist!

But now I decide that the ArrayList is not good, because I want to maintain the items ordered, so I substitute it with a TreeSet in this way:

Set<Car> car = new TreeSet<Car>(new Comparator<Car>() {
@Override 
public int compare(Car car1, Car car2) {

    int compPrice = - Float.compare(car1.getPrice(), car2.getPrice());

    if (compPrice > 0 || compPrice < 0)
        return compPrice;
    else
        return car1.getModel().compareTo(car2.getModel());                                  

}});

car.add(new Car("carA",100f));
car.add(new Car("carB",101f));
car.add(new Car("carC",110f));

System.out.println(a.contains(new Car("carB",111f)));

But now there is a problem, it return FALSE... why?

It seems that when I invoke contains() using an arrayList the method equals() is invoked. But it seems that when I invoke contains() using a TreeSet with a comparator, the comparator is used instead.

Why does that happen?

A TreeSet is implicitly sorted, and it uses a Comparator for this sorting. The equals() method can only tell you if two objects are the same or different, not how they should be ordered for sorting. Only a Comparator can do that.

More to the point, a TreeSet also uses comparisons for searching . This is sort of the whole point of tree-based map/set. When the contains() method is called, a binary search is performed and the target is either found or not found, based on how the comparator is defined. The comparator defines not only logical order but also logical identity. If you are relying on logical identity defined by an inconsistent equals() implementation, then confusion will probably ensue.

TreeSet forms a binary tree keeping elements according to natural (or not) orders, so in order to search quickly one specific element is the collection, TreeSet uses Comparable or Comparator instead of equals() .

As TreeSet JavaDoc precises:

Note that the ordering maintained by a set (whether or not an explicit comparator is provided) must be consistent with equals if it is to correctly implement the Set interface. (See Comparable or Comparator for a precise definition of consistent with equals.) This is so because the Set interface is defined in terms of the equals operation, but a TreeSet instance performs all element comparisons using its compareTo (or compare) method, so two elements that are deemed equal by this method are, from the standpoint of the set, equal. The behavior of a set is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Set interface.

We can find a similarity with the HashCode/Equals contract:

If equals() returns true , hashcode() has to return true too in order to be found during search.

Likewise with TreeSet :

If contains() (using Comparator or Comparable ) returns true , equals() has to return true too in order to be consistent with equals() .

THEREFORE: Fields used within TreeSet.equals() method have to be exactly the same (no more, no less) than within your Comparator implementation.

The reason for the different behaviour is, that you consider the price member in the compare method, but ignore it in equals.

new Car("carB",101f)     // what you add to the list   
new Car("carB",111f)     // what you are looking for

Both instances are "equals" (sorry...) since their model members are equal (and the implementation stops after that test). They "compare" as different, though, because that implementation also checks the price member.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM