II无法理解在实现Comparable接口时,类的自然排序应该如何“与equals一致”。我在程序中发现了一个缺陷,因此我在界面Comparable的文档中检查了它。我的问题是虽然在equals方法的基础上将两个对象视为不同,但TreeMap结构将它们视为相等,因此不接受第二个插入。示例代码为:
public class Car implements Comparable<Car> {
int weight;
String name;
public Car(int w, String n) {
weight=w;
name=n;
}
public boolean equals(Object o){
if(o instanceof Car){
Car d = (Car)o;
return ((d.name.equals(name)) && (d.weight==weight));
}
return false;
}
public int hashCode(){
return weight/2 + 17;
}
public String toString(){
return "I am " +name+ " !!!";
}
public int compareTo(Car d){
if(this.weight>d.weight)
return 1;
else if(this.weight<d.weight)
return -1;
else
return 0;
}
/*public int compareTo(Car d){
return this.name.compareTo(d.name);
}*/
}
public static void main(String[] args) {
Car d1 = new Car(100, "a");
Car d2 = new Car(110, "b");
Car d3 = new Car(110, "c");
Car d4 = new Car(100, "a");
Map<Car, Integer> m = new HashMap<Car, Integer>();
m.put(d1, 1);
m.put(d2, 2);
m.put(d3, 3);
m.put(d4, 16);
for(Map.Entry<Car, Integer> me : m.entrySet())
System.out.println(me.getKey().toString() + " " +me.getValue());
TreeMap<Car, Integer> tm = new TreeMap<Car, Integer>(m);
System.out.println("After Sorting: ");
for(Map.Entry<Car, Integer> me : tm.entrySet())
System.out.println(me.getKey().toString() + " " +me.getValue());
}
输出结果为:
I am a !!! 16
I am c !!! 3
I am b !!! 2
After Sorting:
I am a !!! 16
I am c !!! 2
也就是说,对象c已经(稍微)替换了对象b。 如果我注释原始的equals方法并取消注释第二个equals方法,它根据名称比较对象,则输出是预期的:
I am a !!! 16
I am c !!! 3
I am b !!! 2
After Sorting:
I am a !!! 16
I am b !!! 2
I am c !!! 3
为什么它以这种方式出现,为了在TreeMap中插入和排序具有相同值的某些属性的不同对象,我应该改变什么?
答案 0 :(得分:4)
当两个权重相等时,compareTo()
需要检查名称:
public int compareTo(Car d){
if(this.weight>d.weight)
return 1;
else if(this.weight<d.weight)
return -1;
return this.name.compareTo(d.name);
}
这将使compareTo()
与equals()
一致(后者现在可以根据前者重写)。此外,如果名称不同,地图将允许具有相同权重的多个条目。
答案 1 :(得分:1)
也就是说,对象c已经(稍微)替换了对象b。
是的,它会的。它们具有相同的权重,因此TreeMap
认为它们是相等的。地图永远不会包含两个“相等”的键(你会如何查找一个值?),因此一个替换另一个。
如果您不希望它们被视为平等,则需要使compareTo
方法区分它们(例如,使用name
作为辅助排序顺序)。
documentation for TreeMap
解释说,如果您的compareTo
方法与您的equals
方法不一致(不是这样),您将无法获得正常的Map
行为:< / p>
请注意,如果此有序映射要正确实现Map接口,则树映射维护的顺序(如任何有序映射)以及是否提供显式比较器必须与equals一致。 (有关与equals一致的精确定义,请参阅Comparable或Comparator。)这是因为Map接口是根据equals操作定义的,但是有序映射使用compareTo(或compare)方法执行所有键比较,因此从排序映射的角度来看,通过此方法被视为相等的键是相等的。即使排序与equals不一致,也可以很好地定义有序映射的行为。它只是没有遵守Map接口的一般合同。
答案 2 :(得分:0)
您的compareTo()
方法不是consistent with equals()
:
当且仅当
c.compare(e1, e2)==0
与e1.equals(e2)
和e1
[...]的e2
具有相同的布尔值。
请改为尝试:
public int compareTo(Car d){
if(this.weight>d.weight)
return 1;
else if(this.weight<d.weight)
return -1;
else
return this.name.compareTo(d.name);
}
在原始实现中,当比较器具有相同的weight
但不同的name
时,它们在比较器方面被认为是相等的,而它们在equals()
方面则不同。
答案 3 :(得分:0)
Comparable接口doc说“当且仅当e1.compareTo(e2)== 0具有与e1.equals(e2)相同的布尔值时,C类的自然排序被认为与equals一致。 C类的e1和e2“。但是你的compareTo()没有那样做,因为它没有检查name字段的相等性。如果您对compareTo()进行检查,则可以正常工作。