A
の要素のSet
があります。Setに不変要素のみが含まれるべきですか?
私のコードでは、セットに含まれる要素を変更すると臭いがあります(この変更により、a
のIDが変更される可能性があることを考慮してください)。
A
の要素のSet
があります。Setに不変要素のみが含まれるべきですか?
私のコードでは、セットに含まれる要素を変更すると臭いがあります(この変更により、a
のIDが変更される可能性があることを考慮してください)。
。 the JavaDoc of the Set interfaceから
:
注:可変オブジェクトがセット要素として使用されている場合細心の注意を払わなければならない。オブジェクトがセット内の要素であるときに等価比較に影響するようにオブジェクトの値が変更された場合、セットの動作は指定されていません。この禁止の特別な場合は、セットがそれ自身を要素として含むことは許されないということです。
強調鉱山。
equals()
に影響を与えずに要素を変更できる場合は、イコールの実装が正しいかどうかを強く再考します。
'equals()'の実装は、それを呼び出すコードを書くプログラマーを驚かせることがなければ_correct_です。 (それはあなたのクラスのメンバーを 'Set'で使うプログラマーなら驚くべきことではありません。)あなたがクラスを使うコードを書く唯一の人なら、あなたの意見だけが重要です。一方で、あなたのコードを他の人が再利用しようとするならば、他の人が 'equals()'が意味することを期待していることをより深く考えたいかもしれません。 –
私は同意しません。私は 'equals'の実装はすべてのフィールドを比較するべきだと考えています。これは普遍的に理解される*唯一の実装です。オブジェクトのフィールドのサブセットを比較する場合は、別の名前付きメソッド、 'isSamePlaceAs()'、 'isSameSizeAs()'などを追加する必要があります。 – Michael
Set
の中の変更可能な要素を変更することで、不変なものは間違いなくの優先です。
たとえば、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; }
}
あなたの例では、それが表示されていなくても、対応する 'equals'の実装を前提としています。 –
@LewBlochこの例では動作する必要はありませんが、通常は 'equals'を上書きします。 – assylias
'equals'メソッドが間違った答えを返した場合、' Set'はアイテムをどのように見つけますか?はい、この例では動作する必要があります。ハッシュされたコレクションは 'hashCode'と' equals'の両方を使うことを忘れないでください。一致しない場合は、コードが破損します。実際には、あなたは壊れたコードが受け入れられると主張しています。 –
フィールドが変更されて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;
}
}
}
あなたの例では、 'equals'の実装が仮定されていますそれを表示しない –
ありがとうルー、修正されました。 – kerner1000
イディオムの提案: 'return i == other.i'。 'boolean'を決めるのに' if'で 'boolean'を使うのはやや冗長です。 –
それだけで香りがしません。それは壊れます。 –
は、hashCode/equalsの計算にどのフィールドが使用され、どのフィールドが変更されるかによって異なります。 – kerner1000