6

この質問を読みました:セット内の要素を変更すると、「等しい」セマンティクスが変更されます

ただし、HashSet 内の項目を変更して後で削除できないという問題を解決する方法がわかりません。

私はいくつかのサンプルソースコードを持っています:

public static void main(String[] args) {
    TestClass testElement = new TestClass("1");
    Set<TestClass> set = new HashSet<>();
    set.add(testElement);
    printIt(testElement, set, "First Set");
    testElement.setS1("asdf");
    printIt(testElement, set, "Set after changing value");
    set.remove(testElement);
    printIt(testElement, set, "Set after trying to remove value");
    testElement.setS1("1");
    printIt(testElement, set, "Set after changing value back");
    set.remove(testElement);
    printIt(testElement, set, "Set removing value");
}

private static void printIt(TestClass hullo, Set<TestClass> set, String message) {
    System.out.println(message + " (hashCode is " + hullo.hashCode() + "):");
    for (TestClass testClass : set) {
        System.out.println("    " + testClass.toString());
        System.out.println("        HashCode: " + testClass.hashCode());
        System.out.println("        Element is equal: " + hullo.equals(testClass));
    }
}

TestClass は、変数 (および getter と setter) を保持し、hashcode() と equals() が実装されている単なる POJO です。

equals() および hashcode() メソッドを示してほしいというリクエストがありました。これらはEclipseによって自動生成されます:

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((s1 == null) ? 0 : s1.hashCode());
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    TestClass other = (TestClass) obj;
    if (s1 == null) {
        if (other.s1 != null)
            return false;
    } else if (!s1.equals(other.s1))
        return false;
    return true;
}

結果は次のとおりです。

First Set (hashCode is 80):
    TestClass [s1=1]
        HashCode: 80
        Element is equal: true
Set after changing value (hashCode is 3003475):
    TestClass [s1=asdf]
        HashCode: 3003475
        Element is equal: true
Set after trying to remove value (hashCode is 3003475):
    TestClass [s1=asdf]
        HashCode: 3003475
        Element is equal: true
Set after changing value back (hashCode is 80):
    TestClass [s1=1]
        HashCode: 80
        Element is equal: true
Set removing value (hashCode is 80):

ハッシュコードが変更された場合、HashSet から値を削除できません。リンクされた質問のように、なぜそうなのかはわかりますが、変更された値を削除する方法がわかりません。そうする可能性はありますか?

4

3 に答える 3

8

ハッシュセットのキーは不変ではないため、問題に直面しています。不変のキーがない場合は、元のキー オブジェクトの参照が変更されると失われます。そして、それを処理することはできません。これは、コレクションのメモリ リークと呼ばれることもあります。したがって、不変のキーを使用すると、このような状況に陥ることはありません。

于 2013-06-20T09:23:57.670 に答える
2

詳細にリンクした質問として、および他の人が指摘したように、変更可能なキーの問題が発生しています。Javadocから再引用します:

注: ミュータブル オブジェクトをセット要素として使用する場合は、細心の注意を払う必要があります。オブジェクトがセット内の要素であるときに、オブジェクトの値が equals 比較に影響を与える方法で変更された場合、セットの動作は指定されません。

ご指摘の通り、わかります。問題は、その場合にオブジェクトを実際にどのように削除するかです。Set.remove()オブジェクトがハッシュ テーブルで失われているため、 を使用できません。ただし、 を使用Iteratorしてそれを行うことができます。次のようなもの:

TestClass toRemove = <the same instance, but mutated>;
for (Iterator<TestClass> iter = set.iterator(); iter.hasNext(); ) {
  TestClass item = iter.next();
  if (toRemove.equals(item)) {
    iter.remove();
  }
}

equals()このアプローチは、使用しているような標準メソッドにはインスタンス チェックがあり、そのチェックが true を返すという事実に依存しています。

これは、この問題を解決する正しい方法ではないことに注意してください。正しい方法は、不変のキーを使用するか、「細心の注意を払う」ことですが、これは、変更されたオブジェクトを .xml ファイルから削除する方法HashSetです。

于 2013-06-20T14:04:24.990 に答える