1つのオブジェクトを作成し、それをArrayList
に追加したとします。それからまったく同じコンストラクタ入力で別のオブジェクトを作成した場合、contains()
メソッドは2つのオブジェクトが同じであると評価しますか?コンストラクタが入力に対して何も変わらず、両方のオブジェクトに格納されている変数が同一であると仮定します。
ArrayList<Thing> basket = new ArrayList<Thing>();
Thing thing = new Thing(100);
basket.add(thing);
Thing another = new Thing(100);
basket.contains(another); // true or false?
class Thing {
public int value;
public Thing (int x) {
value = x;
}
equals (Thing x) {
if (x.value == value) return true;
return false;
}
}
contains()
がclass
を返すようにするには、true
をどのように実装する必要がありますか。
ArrayList implements
リストインタフェース。
List
メソッドの Javadoc for contains
を見ると、2つのオブジェクトが同じかどうかを評価するためにequals()
メソッドが使用されていることがわかります。
正しい実装は
public class Thing
{
public int value;
public Thing (int x)
{
this.value = x;
}
@Override
public boolean equals(Object object)
{
boolean sameSame = false;
if (object != null && object instanceof Thing)
{
sameSame = this.value == ((Thing) object).value;
}
return sameSame;
}
}
ArrayListはクラス(あなたのケースThingクラス)に実装されたequalsメソッドを使ってequals比較を行います。
パフォーマンスを向上させるためだけであっても、通常はhashCode()
をオーバーライドするたびにequals()
をオーバーライドする必要があります。 HashCode()
は比較の際にあなたのオブジェクトがどの「バケツ」に分類されるかを決定するので、equal()
がtrueと評価する2つのオブジェクトは同じhashCode
value()
を返すべきです。私はhashCode()
のデフォルトの振る舞いを思い出すことができません(それが0を返すなら、あなたのコードはゆっくり動くはずですが、それがアドレスを返すなら、あなたのコードは失敗するでしょう)。私はhashCode()
をオーバーライドするのを忘れたので私のコードが失敗した時の束を覚えています。 :)
オブジェクトに対してequalsメソッドを使用します。そのため、Thingがequalsをオーバーライドして、比較のためにオブジェクトに格納されている変数を使用しない限り、contains()
メソッドにtrueは返されません。
value
がプリミティブ型ではない場合、次の実装は間違っていることに注意してください。
public class Thing
{
public Object value;
public Thing (Object x)
{
this.value = x;
}
@Override
public boolean equals(Object object)
{
boolean sameSame = false;
if (object != null && object instanceof Thing)
{
sameSame = this.value == ((Thing) object).value;
}
return sameSame;
}
}
その場合、私は以下を提案します。
public class Thing {
public Object value;
public Thing (Object x) {
value = x;
}
@Override
public boolean equals(Object object) {
if (object != null && object instanceof Thing) {
Thing thing = (Thing) object;
if (value == null) {
return (thing.value == null);
}
else {
return value.equals(thing.value);
}
}
return false;
}
}
class Thing {
public int value;
public Thing (int x) {
value = x;
}
equals (Thing x) {
if (x.value == value) return true;
return false;
}
}
あなたが書く必要があります:
class Thing {
public int value;
public Thing (int x) {
value = x;
}
public boolean equals (Object o) {
Thing x = (Thing) o;
if (x.value == value) return true;
return false;
}
}
今ではうまくいきます;)
他のポスターは、contains()がどのように機能するかについての質問に答えています。
あなたの質問の同様に重要な側面は、equals()を正しく実装する方法です。そしてこれに対する答えは本当にこの特定のクラスのオブジェクトの同等性を構成するものに依存しています。あなたが提供した例で、両方ともx = 5を持つ2つの異なるオブジェクトがある場合、それらは等しいですか?それは本当にあなたがやろうとしていることによります。
オブジェクトの同等性のみに関心がある場合は、.equals()の default 実装(Objectによって提供されるもの)は、アイデンティティーのみを使用します(つまり、this == other)。それがあなたが望むものであるならば、ただあなたのクラスにequals()を実装しないでください(それをObjectから継承させてください)。あなたが書いたコードは、あなたがアイデンティティのために行っているのであれば一種の正しいですが、実際のクラスb/cには決して現れないでしょう。
この記事を読み始めたばかりの場合は、Joshua BlochによるEffective Javaの本を強くお勧めします。これは素晴らしい読み物であり、この種のこと(さらに、アイデンティティーベースの比較以上のことをしようとしているときにequals()を正しく実装する方法)についても説明しています。
JavaDoc :からのショートカット
boolean (Object o) を含む
このリストに指定された要素が含まれている場合はtrueを返します。より正式には、このリストが (o == null?e == null:o.equals(e)) のような要素eを少なくとも1つ含む場合に限りtrueを返します