Java 使用Compariable比较对象并在树状图中对其排序

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

我无法理解在实现接口时,类的自然顺序应该如何“与equals一致”。我在我的程序中检测到一个缺陷,因此我在界面的文档中检查了它。我的问题是,尽管在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());
}
也就是说,对象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);
}