2

Comparableインターフェイスを実装するときに、クラスの自然な順序付けを「equals と一致させる」方法を理解できません。プログラムに欠陥を発見したため、インターフェイスComparableのドキュメントで確認しました。私の問題は、2 つのオブジェクトが equals メソッドのベースで異なると見なされているにもかかわらず、TreeMap 構造がそれらを等しいものとして扱い、その結果、2 番目の挿入を受け入れないことです。サンプルコードは次のとおりです。

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 メソッドをコメントし、名前に従ってオブジェクトを比較する 2 番目の 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 に同じ値の属性を持つさまざまなオブジェクトを挿入して並べ替えるには、何を変更すればよいですか?

4

4 に答える 4

4

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()また、名前が異なる場合、マップは同じ重みを持つ複数のエントリを許可します。

于 2012-12-01T11:05:26.957 に答える
1

つまり、オブジェクトcがオブジェクトbを(ある程度)置き換えたということです。

はい、そうです。それらは等しい重みを持っているので、それらは等しいTreeMapと見なします。マップに2つの「等しい」キーが含まれることはありません(値をどのように検索しますか?)。したがって、一方が他方に置き換わります。

それらが等しいと見なされたくない場合は、compareToメソッドでそれらを区別する必要があります(たとえばname、2次ソート順として使用することによって)。

ドキュメントでTreeMapcompareToは、メソッドがメソッドと一致していない場合equals(そうでない場合)、通常のMap動作が得られないと説明しています。

ソートされたマップと同様に、ツリーマップによって維持される順序、および明示的なコンパレータが提供されているかどうかは、このソートされたマップがMapインターフェイスを正しく実装するためには、equalsと一致している必要があることに注意してください。(equalsとの整合性の正確な定義については、ComparableまたはComparatorを参照してください。)これは、Mapインターフェイスがequals操作で定義されているためですが、ソートされたマップは、compareTo(またはcompare)メソッドを使用してすべてのキー比較を実行します。この方法で等しいと見なされるキーは、ソートされたマップの観点からは等しいです。ソートされたマップの動作は、その順序がequalsと矛盾している場合でも明確に定義されています。Mapインターフェースの一般的な契約に従わないだけです。

于 2012-12-01T11:04:00.333 に答える
1

あなたのcompareTo()方法は と一致してequals()いません:

if and only ifは、 everyおよび[...]と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);
}

元の実装では、2 つのオブジェクトが同じであるweightが異なる場合、コンパレータに関しては等しいと見なされますがname、 に関しては異なりequals()ます。

于 2012-12-01T11:05:15.040 に答える
0

Comparable インターフェイスのドキュメントには、「クラス C の自然順序付けは、e1.compareTo(e2) == 0 がすべての e1 および e2 に対して e1.equals(e2) と同じブール値を持つ場合にのみ、equals と一致すると言われています。クラスCの」。しかし、名前フィールドの等価性をチェックしないため、compareTo() はそれを行いません。そのためのチェックをcompareTo()に入れると、機能します。

于 2012-12-01T11:15:51.277 に答える