2

有一个 Java bean Car 可能包含两个值:型号和价格。

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

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


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

这允许我以这种方式检查数组列表是否已经包含相同型号的 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() 时,似乎使用了比较器。

为什么会这样?

4

3 回答 3

3

TreeSet形成一棵根据自然(或非)顺序保存元素的二叉树,因此为了快速搜索一个特定元素是集合,TreeSet使用ComparableorComparator代替equals().

正如TreeSet JavaDoc 所说:

请注意,如果要正确实现 Set 接口,集合维护的顺序(无论是否提供显式比较器)必须与 equals 一致。(参见 Comparable 或 Comparator 以了解与 equals 一致的精确定义。)这是因为 Set 接口是根据 equals 操作定义的,但 TreeSet 实例使用其 compareTo(或 compare)方法执行所有元素比较,所以两个从集合的角度来看,这种方法认为相等的元素是相等的。一个集合的行为是明确定义的,即使它的顺序与equals不一致;它只是不遵守 Set 接口的一般约定。

我们可以找到与 HashCode/Equals 合约的相似之处:

如果equals()返回truehashcode() 也必须返回 true 才能在搜索期间找到。

同样与TreeSet

如果contains()(使用Comparatoror Comparable)返回true,为了与一致equals() 也必须返回。trueequals()

因此:方法中使用的字段TreeSet.equals()必须与您的实现中使用的字段完全相同(不多也不少)Comparator

于 2012-12-07T01:09:45.100 回答
3

ATreeSet是隐式排序的,它使用 aComparator进行排序。该equals()方法只能告诉您两个对象是否相同或不同,而不能告诉您它们应该如何排序以进行排序。只有一个Comparator可以做到。

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

于 2012-12-07T00:38:42.093 回答
0

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

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

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

于 2012-12-07T00:54:32.847 回答