Java 使用Compariable比较对象并在树状图中对其排序
我无法理解在实现接口时,类的自然顺序应该如何“与equals一致”。我在我的程序中检测到一个缺陷,因此我在界面的文档中检查了它。我的问题是,尽管在equals方法的基础上,两个对象被认为是不同的,但TreeMap结构将它们视为相等的,因此不接受第二个插入。示例代码是:Java 使用Compariable比较对象并在树状图中对其排序,java,equals,treemap,comparable,Java,Equals,Treemap,Comparable,我无法理解在实现接口时,类的自然顺序应该如何“与equals一致”。我在我的程序中检测到一个缺陷,因此我在界面的文档中检查了它。我的问题是,尽管在equals方法的基础上,两个对象被认为是不同的,但TreeMap结构将它们视为相等的,因此不接受第二个插入。示例代码是: public class Car implements Comparable<Car> { int weight; String name; public Car(int w, String n) {
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());
}
也就是说,对象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
为什么会出现这种情况?为了在树形图中插入和排序具有相同属性的不同对象,我应该修改什么
也就是说,对象c已经(在某种程度上)替换了对象b
是的,可以。它们的权重相等,因此TreeMap
认为它们相等。映射从不包含两个“相等”键(如何查找值?),因此一个键替换另一个键
如果您不希望将它们视为相等,则需要使用比较方法来区分它们(例如,使用名称
作为次要排序顺序)
说明如果您的compareTo
方法与equals
方法不一致(事实并非如此),您将无法获得正常的Map
行为:
请注意,与任何排序映射一样,树映射维护的顺序,以及是否提供了显式比较器,如果此排序映射要正确实现映射接口,则必须与equals一致。(请参阅Comparable或Comparator以获得与equals一致的精确定义。)这是因为映射接口是根据equals操作定义的,但排序映射使用其compareTo(或compare)方法执行所有键比较,因此从排序映射的角度来看,此方法认为相等的两个键,平等。排序映射的行为定义良好,即使其顺序与equals不一致;只是没有遵守地图界面的总合同
您的compareTo()
方法不是:
当且仅当c.compare(e1,e2)==0
具有与e1相同的布尔值。对于每个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);
}
公共内部比较(d车){
如果(此重量>d重量)
返回1;
否则如果(this.weight当两个权重相等时,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);
}
公共内部比较(d车){
如果(此重量>d重量)
返回1;
else if(this.weight可比接口文档说“类C的自然顺序与equals一致当且仅当e1.compareTo(e2)==0对于类C的每个e1和e2具有与e1.equals(e2)相同的布尔值。”不会这样做,因为它不会检查名称字段的相等性。如果您在compareTo()中进行检查,它会起作用。我想,既然Map(HasMap)接受给定的元素,因为它们具有不同的名称,所以也应该将它们视为TreeMap。HashMap使用equals方法区分两个对象。我认为TreeMap仅使用compareTo进行排序,而不使用compareTo来接受或拒绝元素的插入。@arjacsoh:您阅读了TreeMap?它对此进行了一些详细说明。从TreeMap文档中:“但是一个映射使用其compareTo(或compare)方法执行所有键比较,因此从排序映射的角度来看,被此方法视为相等的两个键是相等的。”。因此它使用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);
}