4

hashMap からインスタンスを格納および取得するために、クラス (Dog) で hashCode() および equals() をオーバーライドしました。コードは次のとおりです。

class Dog {

public Dog(String n) {
    name = n;
}
public String name;

public boolean equals(Object o) {
    if ((o instanceof Dog)
            && (((Dog) o).name == name)) {
       return true;
    } else {
       return false;
    }
}

public int hashCode() {
    return name.length();
   }
}

hashMap コードは次のとおりです。

public class MapTest {

public static void main(String[] args) {
    Map<Object, Object> m = new HashMap<Object, Object>();
    m.put("k1", new Dog("aiko"));
    Dog d1 = new Dog("clover");
    m.put(d1, "Dog key");    // #1
    System.out.println(m.get("k1"));
    String k2 = "k2";
    d1.name = "arthur";     // #2
    System.out.println(m.get(d1)); #3 
    System.out.println(m.size()); 
  }
}

問題は、2 で hashMap 内に保存されている犬オブジェクトの名前を 1 で変更したことです。3 で期待される出力は NULL ですが、実際は Dog Key です!! equals() メソッドで clover!=arthur として失敗することを期待していますが、成功しました!! hashCode が成功すると (つまり、長さ ==6)、equals() メソッドが失敗しても、マップに格納されている値が取得されることに気付きました。代わりに == を変更して equals() を使用しましたが、変更は発生せず、問題は残ります。 .

4

3 に答える 3

5

参照を比較する == ではなく .equals() を使用して文字列を比較します。

public boolean equals(Object o) {
    if ((o instanceof Dog)
            && (((Dog) o).name.equals(name))) {
       return true;
    } else {
       return false;
    }
}

また、そのequalsメソッドは少しずれています。name が null の場合はどうなりますか? null ポインター例外が発生します。その特殊なケースには、別のチェックを追加する必要があります。

于 2012-08-01T22:48:29.470 に答える
3

なぜequals「決して失敗しない」のですか?

トムのコメントによると:

..マップ内のオブジェクトを変更してもキーを一定に保つと、同じキーインスタンスを使用して値を取得できます。dog.equals(dog)は、コードで常にtrueになります(同時変更がない限り)

つまり、次の行です。

d1.name = "arthur"; 

すでにHashMapにあるオブジェクトを変更しています。と比較してください(t「印刷」がtrueまたはfalseの場合):

Dog d1 = new Dog("clover");
// what "put" is effectively doing: there is *no* Copy/Clone
Dog inMap = d1;
t(inMap == d1);                 // true: same object, reference equality!
d1.name = "arthur";
t(inMap.name.equals("arthur")); // true: same object! "name" member was *mutated*
t(d1.equals(inMap));            // true: same object!

したがって、equalsオブジェクトをそれ自体と比較しているため、失敗することはありません:)

私も最初はそれを見逃しました。JavaにはCallByObject-Sharingセマンティクスがあることを思い出してください。つまり、メソッドに渡されるオブジェクト暗黙的なコピー/クローン/複製はありません。

それでは、それを失敗させる方法:

Dog d1 = new Dog("clover");
Dog d2 = new Dog("clover");
t(d1 == d2);                   // false: different objects!
m.put(d1, "Dog key");          // put in with D1 object
System.out.println(m.get(d1)); // "Dog key"   -okay, equals
System.out.println(m.get(d2)); // "Dog key"   -okay, equals
d2.name = "arthur";            // *mutate* D2 object
t(d1.equals(d2));              // false: no longer equal
System.out.println(m.get(d1)); // "Dog key"   -okay, always equals, as per above
System.out.println(m.get(d2)); // ""          -no good, no longer equals

そして、どのようにhashCode適合しますか?

ハッシュコードは、キー(および値のペア)を配置するハッシュテーブルバケットを決定するために使用されます。ルックアップ(またはセット)を実行するとき、バケットは最初にハッシュコードによってルックアップされ、次に各キーがすでにバケットにマップされていますでチェックされequalsます。バケットにキーがない場合、equalsは呼び出されません。

nameこれは、元の投稿で長さ8の文字列に変更すると、ルックアップが失敗する理由を説明しています。のバケット(たとえば、空のバケット)が最初に選択されるため、他のバケットequalsに存在する既存のキーに対して呼び出されることはありません。。同じオブジェクトキーすでに存在している可能性がありますが、見たことはありません。

それでは、別のハッシュコードで失敗させる方法は次のとおりです。

Dog d1 = new Dog("clover");
m.put(d1, "Dog key");          // put in with D1 object, hashCode = 6
System.out.println(m.get(d1)); // "Dog key" -okay, hashCode = 6, equals
d1.name = "Magnolia";          // change value such that it changes hash code
System.out.println(m.get(d1)); // ""        -fail, hashCode = 8, equals
// ^-- attaching a debugger will show d1.equals is not called

したがって、キーがハッシュテーブル(HashMapなど)で見つかるには、次のようになっている必要があります。

k.hashCode() == inMap.hashCode() && k.equals(inMap);

同じバケットにマップする多くのハッシュコードが存在する可能性があります。ただし、上記はルックアップ成功することを保証する唯一の方法です。


もちろん、一般的な文字列を比較する正しい方法については、他の返信を参照してください。

于 2012-08-01T23:08:34.367 に答える
1

文字列を と比較する問題は別として==、何が起こっているかというと、犬の名前を変更することです

Dog d1 = new Dog("clover");
m.put(d1, "Dog key");    // #1
System.out.println(m.get("k1"));
String k2 = "k2";
d1.name = "arthur";     // #2

同じ長さのものに。したがって、マップ内のルックアップは同じハッシュバケットを検索し、もちろんそこに犬が見つかります。

名前を異なる長さの名前に変更すると、(通常) 別のバケットが検索され、犬が見つかりません。

于 2012-08-01T23:00:20.417 に答える