当前位置: 首页 > 知识库问答 >
问题:

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

房唯
2023-03-14

当实现可比接口时,我不明白类的自然顺序应该如何“与相等的一致”。我在我的程序中检测到一个缺陷,因此我在接口可比的文档中检查了它。我的问题是,虽然两个Object在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

为什么会出现这种情况?为了在树形图中插入和排序具有相同属性的不同对象,我应该修改什么?

共有3个答案

景国兴
2023-03-14

也就是说,对象c已经(在某种程度上)替换了对象b。

是的,可以。它们的权重相等,因此TreeMap认为它们相等。映射从不包含两个“相等”键(如何查找值?),因此,一个替代另一个。

如果您不希望将它们视为相等,则需要使用比较方法来区分它们(例如,使用名称作为次要排序顺序)。

TreeMap的文档说明,如果compareTo方法与equals方法不一致(事实并非如此),则无法获得正常的Map行为:

请注意,与任何排序映射一样,树映射维护的顺序,以及是否提供了显式比较器,如果此排序映射要正确实现映射接口,则必须与equals一致。(参见可比或比较器,了解与等于一致的精确定义。)这是因为映射接口是根据equals操作定义的,但排序映射使用其compareTo(或compare)方法执行所有键比较,因此从排序映射的角度来看,此方法认为相等的两个键是相等的。排序映射的行为定义良好,即使其顺序与equals不一致;只是没有遵守地图界面的总合同。

陈马鲁
2023-03-14

您的compareTo()方法与equals()不一致:

当且仅当c.compare(e1,e2)==0具有与e1相同的布尔值。对于每个e1e2[…],等于(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);
}

在最初的实现中,当两个对象具有相同的权重但不同的名称,而它们在equals()方面不同时,就比较器而言,它们被认为是相等的。

赖翰
2023-03-14

当两个权重相等时,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()一致(后者现在可以根据前者重写)。此外,如果名称不同,地图将允许具有相同权重的多个条目。

 类似资料:
  • 问题内容: 最近在一次求职面试中,有人问我以下问题(对于Java): 鉴于: 的返回值是多少 我回答它会返回false,因为它们是两个不同的对象,并且==是内存地址比较而不是值比较,并且需要使用.equals()比较String对象。但是我被告知,尽管.equals(0方法论是正确的,但是该语句仍然返回true。我想知道是否有人可以向我解释为什么它是正确的,但是为什么我们还在学校里教我们使用equ

  • 如何使用Comparator对对象的ArrayList进行正确排序,然后将排序后的数组传递给自定义数组适配器? 我试过这样做: ArrayList的内容类: 比较器: 在OnCreate中设置适配器: ...因为我使用的是定制的ArrayAdapter,就像: 我需要如何修改此适配器以使其与其他适配器一起工作? 谢谢你的帮助。

  • 我已经被覆盖了hashcode和equals以查找具有相同开始日期和关闭日期的值。我得到了重复对象的类似哈希代码。当等同于这些对象时,我得到了布尔值“假”。在我的理解中,对象==对象比较对象的引用,即使引用是相同的;代码返回假。你能帮我理解是什么问题吗?我在下面发布了我的代码: Main.java 输出: 检查客户对象引用:Customer@643 对象引用(位于Map中):[Customer@6

  • 我需要整理一份arrayList。我可以使用比较器并重写比较函数进行排序。或者我可以自己编写一个合并排序函数,对ArrayList进行排序。 我在某处读到比较器排序使用冒泡排序算法。因为合并排序的复杂度较低,所以我选择合并排序,而不是使用更容易实现的比较器。

  • 我想使用比较器对String类型的ArrayList进行排序。我只找到了如何在ArrayList存储对象时执行此操作的示例。 我有一个包含10个符号的字符串数组列表,最后5个符号是数字。我想按照每个字符串末尾的数字的升序来求解数组列表。我该怎么做? 谢谢

  • 我希望有一个更具体的可比接口,以便我可以更好地键入返回参数。该界面目前如下所示: 现在,当我尝试对这些更具体的可比值列表进行排序时,如下所示,我收到一个错误,因为b的类型是“RankComparable”而不是类型“T”。 为了解决这个问题,我可以实现如下所示的Self方法。这将解决我的问题,但它看起来非常丑陋。如果有人能找到解决这个问题的更好的办法,我会很高兴。