2012-12-01 41 views
1

II无法理解实现Comparable接口时,类的自然排序应该如何“与equals保持一致”。我在程序中发现了一个缺陷,因此我在界面Comparable的文档中检查了它。我的问题是,尽管基于equals方法将两个对象视为不同的,但TreeMap结构将它们视为相等,因此不接受第二个插入。示例代码是:使用Comparable比较对象并在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中插入和排序具有相同值的某些属性的不同对象?

回答

2

当两个权重相等,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

也就是说,对象c已经取代(有点)对象b。

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

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

documentation for TreeMap解释说,如果你compareTo方法与您的equals方法是一致的(它不是),您将无法获得正常的Map行为:

注意,由树状图所维持的顺序,像任何已排序的映射,以及是否提供显式比较器一样,如果此排序映射要正确实现Map接口,则必须与equals保持一致。 (请参阅Comparable或Comparator以获得与equals一致的精确定义)。这是因为Map接口是根据equals操作定义的,但有序映射使用其compareTo(或compare)方法执行所有关键比较,因此两个从排序映射的角度来看,这种方法认为相同的键是相等的。即使排序与等号不一致,排序映射的行为也是很好定义的;它只是不服从Map接口的总体合同。

+0

我想,既然Map(HasMap)接受给定的元素,因为它们是不同的名称,所以也应该对它们进行处理。考虑到equals方法,HashMap区分了两个对象。我认为TreeMap只使用compareTo进行排序,而不是接受或拒绝插入元素。 – arjacsoh

+0

@arjacsoh:您是否阅读过“TreeMap”的文档?它进行了一些细节。 –

+0

根据TreeMap doc:“但是一个映射使用它的compareTo(或compare)方法执行所有的关键比较,所以从排序映射的角度看,这个方法认为相等的两个关键字是相等的。”所以它将compareTo用于“所有关键比较”,而不仅仅用于排序。 – arcy

0

compareTo()方法不consistent with equals()

当且仅当c.compare(e1, e2)==0具有相同的布尔值e1.equals(e2)每一个e1e2 [...]。

试试这个:

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()方面不同。

0

Comparable接口文档说:“对于类C的自然排序被认为与equals相等当且仅当e1.compareTo(e2)== 0具有与e1.equals(e2)相同的布尔值C级的每个e1和e2“。但是你的compareTo()不这样做,因为它不检查name字段的相等性。如果你把这个检查放到compareTo()中,它就可以工作。

相关问题