5

これは私が Java (1.6) で見た中で最もクレイジーなことです:

Set<ActionPlan> actionPlans = assessment.getActionPlans();
//getActionPlans() returns a java.util.HashSet<ActionPlan>
ActionPlan actionPlan = actionPlans.iterator().next();
assertTrue(actionPlan1.equals(actionPlan));
assertEquals(actionPlan1.hashCode(), actionPlan.hashCode());
assertTrue(actionPlans.contains(actionPlan1));

最初の 2 つのアサートは成功しますが、最後のアサートは失敗します。

ActionPlan クラスと Assessment クラスの詳細については説明しません。問題ではないからです。equals と hash が失敗する場合、contains メソッドは失敗します。

Java が壊れているなどと言っているのではありません。おそらく私のコードで何かおかしなことが起こっているのでしょう。

私は経験豊富な Java プログラマーであり、equals と hashCode を実装するためのすべきこととすべきでないことを認識しています。したがって、私のコードに何かが欠けている場合、それは明白なものではありません。

誰もが不可解なものを見たことがありますか?

編集

私は自分のコードでいくつかの調査を行いましたが、問題は休止状態にあると思います。作成後、失敗したアサートが呼び出されるまで、コードのさまざまな部分で ActionPlan オブジェクトの hashCode をログに記録しました。変わりません

また、assessment.getActionPlans() によって返されるクラスを確認しましたが、次のとおりです。

org.hibernate.collection.internal.PersistentSet

この Set の実装は equals または hashcode を適切に使用していないと思いがちです。

誰かがそれについて洞察を持っていますか?

4

3 に答える 3

15

可能な説明があります

  • equals または hashCode を使用しないソート済みセットがあります。
  • equals(Object) の代わりに equals(MyClass) を「オーバーライド」しました
  • hashCode で使用されるフィールドが変更されます。これにより、セットは使用できない状態のままになります。

最後の可能性をテストする最も簡単な方法は、試してみることです

assertTrue(new HashSet(actionPlans).contains(actionPlan1));

これはあなたの場合に合格すると思います。;)


Date には可変であるという欠陥があり、hashCode はその可変フィールドを使用するため、それを変更することでハッシュ コレクションが破損する可能性があります。compareTo で使用されるフィールドを変更すると、同様の問題が発生します。

Set<Date> dates = new HashSet<Date>();
SortedSet<Date> dates2 = new TreeSet<Date>();
Date d1 = new Date(1), d2 = new Date(2), d3 = new Date(3);
dates.add(d1);
dates.add(d2);
dates.add(d3);
dates2.add(d1);
dates2.add(d2);
dates2.add(d3);
d1.setTime(6);
d2.setTime(5);
d3.setTime(4);
System.out.print("The dates contains [");
for (Date date : dates) {
    System.out.print("date " + date.getTime() + " ");
}
System.out.println("]");
System.out.print("The sorted dates2 contains [");
for (Date date : dates2) {
    System.out.print("date " + date.getTime() + " ");
}
System.out.println("]");
for (int i = 1; i <= 6; i++)
    System.out.println("date " + i + " found is " + dates.contains(new Date(i))
            + " and " + dates2.contains(new Date(i)));

版画

The dates contains [date 6 date 5 date 4 ]
The sorted dates2 contains [date 6 date 5 date 4 ]
date 1 found is false and false
date 2 found is false and false
date 3 found is false and false
date 4 found is false and false
date 5 found is false and true
date 6 found is false and false

注: ソートされたコレクションの順序が間違っています。

于 2012-11-28T10:49:31.203 に答える
5

これは、equals をオーバーロードし、オーバーライド しない場合に発生しますequals(Object)

たとえば、次のような場合があります。

public boolean equals(ActionPlan plan) {
    ...
}

それは次のように呼び出されます:

assertTrue(actionPlan1.equals(actionPlan));

...しかし、によって呼び出されることはありませんcontains。必要なもの:

@Override public boolean equals(Object object) {
    ...
}

もちろん、そうではない可能性もあります。コードを見ずに確実に判断する方法はありません。

ActionPlan クラスと Assessment クラスの詳細については説明しません。問題ではないからです。

この回答はその仮定と矛盾しています...代替の障害モードを含むPeterの回答と同様です。これが、短くても完全な例を示すことが常に重要である理由です。

于 2012-11-28T10:48:56.773 に答える