为什么在集合中使用比较器而不是equals()?

时间:2012-12-07 00:28:27

标签: java sorting comparator

有一个java bean Car可能包含两个值:model和price。

现在假设我以这种方式覆盖equals()和hashcode()只检查模型:

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


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

这允许我检查一个arraylist是否已经包含相同型号的商品Car(并且价格无关紧要),这样:

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)));

返回TRUE。那没关系,因为车已经存在了!

但现在我认为ArrayList不好,因为我想维护订购的项目,所以我用这样的方式用TreeSet替换它:

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)));

但是现在有一个问题,它返回FALSE ...为什么?

似乎当我使用arrayList调用contains()时,调用equals()方法。 但似乎当我使用带有比较器的TreeSet调用contains()时,会使用比较器。

为什么会这样?

3 个答案:

答案 0 :(得分:3)

隐式排序TreeSet,并使用Comparator进行此排序。 equals()方法只能告诉您两个对象是相同还是不同,而不是如何排序它们进行排序。只有Comparator可以做到这一点。

更重要的是,TreeSet还使用了搜索的比较。这是基于树的地图/集的整点。调用contains()方法时,将执行二进制搜索,并根据比较器的定义方式找到或找不到目标。比较器不仅定义逻辑顺序而且定义逻辑标识。如果您依赖于由不一致的equals()实现定义的逻辑身份,则可能会出现混淆。

答案 1 :(得分:3)

TreeSet形成一个二叉树,根据自然(或非)顺序保留元素,因此为了快速搜索一个特定元素是集合,TreeSet使用Comparable或{{ 1}}而不是Comparator

作为TreeSet JavaDoc精确:

  

注意由一组维护的排序(无论是否显式   比较器提供)必须与equals一致,如果是的话   正确实现Set接口。 (参见可比较者或比较者   对于与equals一致的精确定义。)就是这样   因为Set接口是根据equals操作定义的,   但是TreeSet实例使用它执行所有元素比较   compareTo(或compare)方法,因此两个被认为相等的元素   从该集合的角度来看,通过这种方法是相等的。该   集合的行为即使其排序不一致也是明确定义的   与...平等它只是没有遵守集合的一般合同   接口

我们可以找到与HashCode / Equals合同的相似性:

如果equals()返回equals()true 必须也会返回true,以便在搜索过程中找到。

hashcode()相同:

如果TreeSet(使用contains()Comparator)返回Comparabletrue 必须返回{{ 1}}也是为了与equals() 一致

因此: true方法中使用的字段必须与equals()实施中的字段完全相同(不多,不少)。

答案 2 :(得分:0)

不同行为的原因是,您在compare方法中考虑price成员,但在equals中忽略它。

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

两个实例都是“等于”(抱歉......),因为他们的模型成员是相同的(并且在该测试之后实现停止)。但是,它们“比较”不同,因为 实现也会检查价格成员。