1

次のことがどのように可能ですか。

void contains(LinkedHashSet data, Object arg) {
    System.out.println(data.getClass()); // java.util.LinkedHashSet
    System.out.println(arg.hashCode() == data.iterator().next().hashCode()); // true
    System.out.println(arg.equals(data.iterator().next())); // true
    System.out.println(new ArrayList(data).contains(arg)); // true
    System.out.println(new HashSet(data).contains(arg)); // true
    System.out.println(new LinkedHashSet(data).contains(arg)); // true (!)
    System.out.println(data.contains(arg)); // false
}

私は何か間違ったことをしていますか?

明らかに、常に発生するとは限りません (単純なオブジェクトのセットを作成しても、それを再現することはできません)。しかし、私の場合、より複雑なクラスの引数を使用すると、常に発生します。

編集:ここで定義しない主な理由は、Eclipse で生成された 20 行と2 倍の長argさのかなり大きなクラスだからです。そして、それは関係ないと思います-2つのオブジェクトが等しい限り。hashCodeequals

4

3 に答える 3

4

独自のオブジェクトを作成し、それらをコレクションで使用する予定がある場合は、常に次のメソッドをオーバーライドする必要があります。

boolean equals(Object o);
int hashCode();

equals のデフォルトの実装は、オブジェクトが同じオブジェクトを指しているかどうかをチェックしますが、内容をチェックするために再定義する必要があるかもしれません。

合理的に実用的である限り、クラス Object によって定義された hashCode メソッドは、個別のオブジェクトに対して個別の整数を返します。ルールを尊重するには、オブジェクトの hashCode が別のオブジェクトと等しい場合は同じでなければならないため、hashCode も再定義する必要があります。

編集hashCode:私は不完全または実装を期待していましたequalsが、あなたの答え以来、キーが HashSet または HashMap に追加された後にキーを変更していることを明らかにしました。

オブジェクトをハッシュ コレクションに追加すると、その hashCode が計算され、コレクション内の物理的な場所にマップするために使用されます。

hashCode の計算に使用されるいくつかのフィールドが変更されると、hashCode 自体が変更されるため、HashSet の実装が混乱します。オブジェクトを取得しようとすると、別の物理的な場所が検索され、オブジェクトが見つかりません。ただし、セットを列挙しても、オブジェクトは引き続き存在します。

このため、常に HashMap または HashSet キーをImmutableにします。

于 2011-11-04T10:42:21.317 に答える
1

とった。一度それがわかれば、答えはあまりに明白で、恥ずかしさで顔を赤らめるしかありません。

static class MyObj {
    String s = "";

    @Override
    public int hashCode() {
        return s.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return ((MyObj) obj).s.equals(s);
    }
}

public static void main(String[] args) {
    LinkedHashSet set = new LinkedHashSet();
    MyObj obj = new MyObj();
    set.add(obj);
    obj.s = "a-ha!";
    contains(set, obj);
}

確実に再現するにはこれで十分です。

説明: hashCode() に使用されるフィールドを決して変更してはなりません!

于 2011-11-04T11:18:34.253 に答える
0

あなたの質問には何かが欠けているようです。私はいくつかの推測をしました:

private void testContains() {
  LinkedHashSet set = new LinkedHashSet();
  String hello = "Hello!";
  set.add(hello);
  contains(set, hello);
}

void contains(LinkedHashSet data, Object arg) {
  System.out.println(data.getClass()); // java.util.LinkedHashSet
  System.out.println(arg.hashCode() == data.iterator().next().hashCode()); // true
  System.out.println(arg.equals(data.iterator().next())); // true
  System.out.println(new ArrayList(data).contains(arg)); // true
  System.out.println(new HashSet(data).contains(arg)); // true
  System.out.println(new LinkedHashSet(data).contains(arg)); // true (!)
  System.out.println(data.contains(arg)); // true (!!)
}

EDITED:質問の変化を追跡するために!

最初の出力以外はすべて「true」になります。「arg」パラメータのタイプについて、より具体的に教えてください。

于 2011-11-04T10:31:59.780 に答える