2017-02-28 4 views
-1

Aの要素のSetがあります。Setに不変要素のみが含まれるべきですか?

私のコードでは、セットに含まれる要素を変更すると臭いがあります(この変更により、aのIDが変更される可能性があることを考慮してください)。

+6

それだけで香りがしません。それは壊れます。 –

+1

は、hashCode/equalsの計算にどのフィールドが使用され、どのフィールドが変更されるかによって異なります。 – kerner1000

答えて

1

the JavaDoc of the Set interfaceから

注:可変オブジェクトがセット要素として使用されている場合細心の注意を払わなければならない。オブジェクトがセット内の要素であるときに等価比較に影響するようにオブジェクトの値が変更された場合、セットの動作は指定されていません。この禁止の特別な場合は、セットがそれ自身を要素として含むことは許されないということです。

強調鉱山。

equals()に影響を与えずに要素を変更できる場合は、イコールの実装が正しいかどうかを強く再考します。

+0

'equals()'の実装は、それを呼び出すコードを書くプログラマーを驚かせることがなければ_correct_です。 (それはあなたのクラスのメンバーを 'Set'で使うプログラマーなら驚くべきことではありません。)あなたがクラスを使うコードを書く唯一の人なら、あなたの意見だけが重要です。一方で、あなたのコードを他の人が再利用しようとするならば、他の人が 'equals()'が意味することを期待していることをより深く考えたいかもしれません。 –

+0

私は同意しません。私は 'equals'の実装はすべてのフィールドを比較するべきだと考えています。これは普遍的に理解される*唯一の実装です。オブジェクトのフィールドのサブセットを比較する場合は、別の名前付きメソッド、 'isSamePlaceAs()'、 'isSameSizeAs()'などを追加する必要があります。 – Michael

1

Setの中の変更可能な要素を変更することで、不変なものは間違いなくの優先です。

3

たとえば、A aをセットに追加した後、aをハッシュコードを変更する方法で変更すると、set.contains(a)がfalseを返す可能性があります。

簡単な例:セットのみimmutablesが含まれている必要があり、時間のほとんど

public static void main(String[] args) { 
    Set<A> set = new HashSet<>(); 
    A a = new A(1); 
    set.add(a); 
    System.out.println(set.contains(a)); //true 
    a.i = 2; 
    System.out.println(set.contains(a)); //false 
} 

static class A { 
    int i; 
    public A(int i) { this.i = i; } 
    @Override public int hashCode() { return i; } 
} 
+0

あなたの例では、それが表示されていなくても、対応する 'equals'の実装を前提としています。 –

+0

@LewBlochこの例では動作する必要はありませんが、通常は 'equals'を上書きします。 – assylias

+0

'equals'メソッドが間違った答えを返した場合、' Set'はアイテムをどのように見つけますか?はい、この例では動作する必要があります。ハッシュされたコレクションは 'hashCode'と' equals'の両方を使うことを忘れないでください。一致しない場合は、コードが破損します。実際には、あなたは壊れたコードが受け入れられると主張しています。 –

0

フィールドが変更されてhashCodeの計算にも使用されると、Setが壊れてしまうため、ほとんどの場合臭いがあります。

Setの要素を変更すると、Setが壊れる可能性があります。

対照的に、Listの要素を変更しても、そのリストは改行されません。

簡単な例(assyliasの答えから取られ、延長)

public class Main { 

    public static void main(String[] args) { 
    Set<A> set = new HashSet<>(); 
    List<A> list = new ArrayList<>(); 
    A a = new A(1); 
    set.add(a); 
    list.add(a); 
    System.out.println(set.contains(a)); // true 
    System.out.println(list.contains(a)); // true 
    a.i = 2; 
    System.out.println(set.contains(a)); // false 
    System.out.println(list.contains(a)); // true 
    } 

    static class A { 
    int i; 

    public A(int i) { 
     this.i = i; 
    } 

    @Override 
    public int hashCode() { 
     return i; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (obj == null) { 
     return false; 
     } 
     if (!(obj instanceof A)) { 
     return false; 
     } 
     A other = (A) obj; 
     if (i != other.i) { 
     return false; 
     } 
     return true; 
    } 
    } 
} 
+0

あなたの例では、 'equals'の実装が仮定されていますそれを表示しない –

+0

ありがとうルー、修正されました。 – kerner1000

+0

イディオムの提案: 'return i == other.i'。 'boolean'を決めるのに' if'で 'boolean'を使うのはやや冗長です。 –

関連する問題