【问题标题】:Using Comparable to compare objects and sorting them in a TreeMap使用 Comparable 比较对象并在 TreeMap 中对它们进行排序
【发布时间】:2012-12-01 10:58:54
【问题描述】:

我无法理解在实现Comparable 接口时,类的自然顺序应该如何“与等号一致”。我在我的程序中检测到一个缺陷,因此我在接口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 中插入和排序具有相同值属性的不同对象,我应该改变什么?

【问题讨论】:

    标签: java equals treemap comparable


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      你的compareTo()方法不是consistent with equals()

      当且仅当c.compare(e1, e2)==0 对于每个e1e2 具有与e1.equals(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】:

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

        是的,会的。它们具有相同的权重,因此TreeMap 认为它们是相等的。地图从不包含两个“相等”的键(你会如何查找一个值?),因此一个替换另一个。

        如果您不希望它们被视为相等,则需要使您的 compareTo 方法区分它们(例如,使用 name 作为辅助排序顺序)。

        documentation for TreeMap 解释说,如果您的 compareTo 方法与您的 equals 方法不一致(事实并非如此),您将无法获得正常的 Map 行为:

        请注意,与任何排序图一样,树状图维护的顺序以​​及是否提供显式比较器,如果该排序图要正确实现 Map 接口,则必须与 equals 保持一致。 (参见 Comparable 或 Comparator 以了解与 equals 一致的精确定义。)这是因为 Map 接口是根据 equals 操作定义的,但排序后的映射使用其 compareTo(或比较)方法执行所有键比较,所以两个从排序映射的角度来看,此方法认为相等的键是相等的。已排序映射的行为是明确定义的,即使它的排序与 equals 不一致;它只是不遵守 Map 接口的一般约定。

        【讨论】:

        • 我想,既然 Map (HasMap) 接受给定的元素,因为它们具有不同的名称,所以也应该对待它们 TreeMap。 HashMap 考虑到 equals 方法来区分两个对象。我认为 TreeMap 使用 compareTo 仅用于排序而不是接受或拒绝元素的插入。
        • @arjacsoh:您阅读过TreeMap 的文档吗?它详细介绍了这一点。
        • 来自 TreeMap 文档:" 但是一个映射使用它的 compareTo(或比较)方法执行所有键比较,所以从排序映射的角度来看,这个方法认为相等的两个键是,平等的。 ”。所以它使用 compareTo 进行“所有关键比较”,而不仅仅是排序。
        【解决方案4】:

        Comparable 接口文档说“当且仅当 e1.compareTo(e2) == 0 具有与 e1.equals(e2) 相同的布尔值C 类的 e1 和 e2。”。但是您的 compareTo() 并没有这样做,因为它不检查名称字段的相等性。如果你在 compareTo() 中检查它,它就可以工作。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-09-26
          • 2016-02-24
          • 1970-01-01
          • 2015-03-24
          • 1970-01-01
          • 2018-05-13
          • 2017-09-20
          相关资源
          最近更新 更多