Java 为什么在集合中使用比较器而不是equals()?
有一个JavaBean汽车可能包含两个值:model和price 现在假设我以这种方式重写equals()和hashcode()只检查模型:Java 为什么在集合中使用比较器而不是equals()?,java,sorting,comparator,Java,Sorting,Comparator,有一个JavaBean汽车可能包含两个值:model和price 现在假设我以这种方式重写equals()和hashcode()只检查模型: public boolean equals(Object o) { return this.model.equals(o.model); } public int hashCode() { return model.hashCode(); } List<Car> car = new ArrayLis
public boolean equals(Object o) {
return this.model.equals(o.model);
}
public int hashCode() {
return model.hashCode();
}
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)));
这允许我检查arraylist是否已经包含同一型号的商品车(价格不重要),方法如下:
public boolean equals(Object o) {
return this.model.equals(o.model);
}
public int hashCode() {
return model.hashCode();
}
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)));
但现在有一个问题,它返回FALSE。。。为什么?
当我使用arrayList调用contains()时,似乎调用了equals()方法。
但是,当我使用带有比较器的树集调用contains()时,似乎使用了比较器
为什么会发生这种情况?一个
TreeSet
是隐式排序的,它使用一个比较器来进行排序。equals()。只有比较器才能做到这一点
更重要的是,TreeSet
还使用比较进行搜索。这是基于树的地图/集合的整个点。调用contains()
方法时,将执行二进制搜索,并根据比较器的定义找到或未找到目标。比较器不仅定义逻辑顺序,还定义逻辑标识。如果你依赖于一个不一致的<代码> >均衡器()/代码>实现所定义的逻辑身份,那么混乱可能会随之发生。 < P>不同行为的原因是,在比较方法中考虑价格成员,但忽略它。p>
new Car("carB",101f) // what you add to the list
new Car("carB",111f) // what you are looking for
这两个实例都是“相等”(抱歉…),因为它们的模型成员是相等的(并且实现在该测试之后停止)。但是,它们“比较”是不同的,因为该实现还检查price成员。TreeSet
形成一个二叉树,根据自然(或非自然)顺序保存元素,因此为了快速搜索,一个特定元素是集合,TreeSet
使用Comparable
或Comparator
而不是equals()
正如JavaDoc所指出的:
请注意,由集合维护的顺序(无论是否为显式
如果提供比较器,则必须与equals一致
正确实现Set接口。(见可比或比较)
对于一个与等于一致的精确定义。)这是如此
因为Set接口是根据equals操作定义的,
但是TreeSet实例使用其
比较法,所以两个元素被认为是相等的
用这种方法,从集合的观点来看,是相等的。这个
即使集合的顺序不一致,集合的行为也是定义良好的
平等;它只是没有遵守集合的总合同
接口
我们可以找到与HashCode/Equals合同的相似之处:
如果equals()
返回true
,hashcode()
也必须返回true才能在搜索过程中找到
同样,对于TreeSet
:
如果contains()
(使用Comparator
或Comparable
)返回true
,equals()
也必须返回true
,以便与equals()
一致
因此:在TreeSet.equals()
方法中使用的字段必须与Comparator
实现中使用的字段完全相同(不多也不少)。在TreeSet javadoc中,我认为它说明了一切:“请注意,由集合维护的顺序(无论是否提供了显式比较器)如果要正确实现Set接口,必须与equals保持一致。“link:ATreeSet
可能不是正确的数据结构。是的,equals()和compare()在bean中处理不同的值。但是调用contains()总是一样的,虽然返回的结果应该不同。虽然我自己尝试回答这个问题,而且我确实明白,原则上二进制搜索在导航树时必须进行比较,但我也有一些OP的困惑。具体来说,Javadoc声明equals()
由contains()
使用。你描述的“合同”记录在哪里?@1111我写道,因为它似乎完全符合逻辑。然而,我同意Javadoc并没有精确地定义它。我猜javadoc使用概念算法o==null相当简单地解释了这个机制?e==null:o.equals(e)
而没有进入实际的实现细节。@111事实上,与equals()
用法相关的奇怪句子只是指Set
接口的contains()
Javadoc。因此,如果需要一个只在“model”上工作的equals(),因此应该使用ArrayList而不是TreeSet,对吗?因为在这种情况下,与任何比较器都没有冲突:我可以将比较器传递给Collections.sort(),在这种情况下,它不在列表中。@user1883212,只要您以不同的方式定义compareTo()
和equals()
方法,您无法保证TreeSet
行为的完美一致性。如果您真的无法更改这些方法(这是非常糟糕的…),那么是的,ArrayList
将是首选,或者更好的HashSet
。