こんなequalsは嫌だ
TRANSCRIPT
大阪 EffectiveJava 読書会 第 2 回2011/06/18 お だ
こんな equals/hashCode
は嫌だ
織田 信亮 ( おだ しんすけ )大阪で 開発者 してます。MS 系の情報を追っかけてますId:odashinsuke @shinsukeodaSQL World (SQL Server のコミュニティ )
日本 Grails/Groovy ユーザグループ
自己紹介
第 3 章 項目 8 equals を… P.33反射的
x.equals(x)対称的
y.equals(x) ⇒ x.equals(y)推移的
x.equals(y) && y.equals(z) ⇒ x.equals(z)整合的
x.equals(y) ⇒ x.equals(y)
文字に置き換えると…反射的
A = A対称的
A = B ⇒ B = A推移的
A = B & B = C ⇒ A = C整合的
上手く表せない orz...
final class 反射的 {
public int id;
@Override
public boolean equals(Object obj) {
if (!(obj instanceof 反射的 )) { return false; }
return this.id == (( 反射的 )obj).id + 1;
}
}
反射的 x = new 反射的 ();
x.id = 1;
System.out.println("x.equals(x):" + x.equals(x)); // false
反射的 でない!
final class 対称的 {
public int value;
@Override
public boolean equals(Object obj) {
if (this == obj) { return true; }
if (obj == null) { return false; }
try {
Field f = obj.getClass().getDeclaredField("value");
f.setAccessible(true);
Object objId = f.get(obj);
return this.value == (Integer)objId;
} catch (Throwable e) { // ガン無視! return false;
}
}
}
対称的 でない!
対称的 x = new 対称的 ();
x.value = 1;
Integer y = Integer.valueOf(1);
System.out.println("x.equals(y):" + x.equals(y)); // true
System.out.println("y.equals(x):" + y.equals(x)); // false
対称的 でない!
class Super推移的 { public int id;
@Override
public boolean equals(Object obj) {
if (this == obj) { return true; }
if (!(obj instanceof Super推移的 )) { return false; }
return this.id == ((Super推移的 )obj).id;
}
}
final class Sub 推移的 extends Super推移的{
public int seq;
@Override
public boolean equals(Object obj) {
if (this == obj) { return true; }
if (!(obj instanceof Super推移的 )) { return false; }
if (obj instanceof Sub 推移的 ) {
return this.id == ((Sub 推移的 )obj).id &&
this.seq == ((Sub 推移的 )obj).seq;
}
return super.equals(obj);
}
}
推移的 でない!
Sub 推移的 x = new Sub 推移的 ();
x.id = 1;
x.seq = 1;
Super 推移的 y = new Super 推移的 ();
y.id = 1;
Sub 推移的 z = new Sub 推移的 ();
z.id = 1;
z.seq = 2;
System.out.println(“x.equals(y):” + x.equals(y)); // true
System.out.println("y.equals(z):" + y.equals(z)); // true
System.out.println("x.equals(z):" + x.equals(z)); // false
推移的 でない!
final class 整合的 {
public int id;
@Override
public boolean equals(Object obj) {
if (this == obj) { return true; }
if (!(obj instanceof 整合的 )) { return false; }
return this.id++ == (( 整合的 )obj).id;
}
}
整合的 x = new 整合的 ();
x.id = 1;
整合的 y = new 整合的 ();
y.id = 1;
System.out.println("x.equals(y):" + x.equals(y)); // true
System.out.println("x.equals(y):" + x.equals(y)); // false
整合的 でない!
final class 例外 {
public int id;
@Override
public boolean equals(Object obj) {
return this.id == (( 例外 )obj).id;
}
}
例外 x = new 例外 ();
x.id = 1;
try {
System.out.println(x.equals(null));
} catch (Throwable e) {
System.out.println(“equals で例外とかありえなーい” ); // こっち通る}
例外を吐く
final class まとも {
public int id;
@Override
public boolean equals(Object obj) {
if (this == obj) { return true; }
if (!(obj instanceof まとも )) { return false; }
return this.id == (( まとも )obj).id;
}
@Override
public int hashCode() { return this.id; }
}
まとも
まとも x = new まとも ();
x.id = 1;
まとも y = new まとも ();
y.id = 1;
まとも z = new まとも ();
z.id = 1;
System.out.println("x.equals(x):" + x.equals(x)); // true
System.out.println("x.equals(y):" + x.equals(y)); // true
System.out.println("y.equals(x):" + y.equals(x)); // true
System.out.println("x.equals(y):" + x.equals(y)); // true
System.out.println("y.equals(z):" + y.equals(z)); // true
System.out.println("x.equals(z):" + x.equals(z)); // true
System.out.println("x.equals(y):" + x.equals(y)); // true
System.out.println("x.equals(y):" + x.equals(y)); // true
まとも
第 3 章 項目 9 equals を… 常に hashCode を… P.45
Object の状態が変わらないなら、 hashCode は常に同じ値を返す
equals での比較結果が true なら、 hashCode を比較しても同じになる
equals での比較結果が false でも、 hashCode が同じになっても良い
final class まとも {
public int id;
@Override
public boolean equals(Object obj) {
if (this == obj) { return true; }
if (!(obj instanceof まとも )) { return false; }
return this.id == (( まとも )obj).id;
}
@Override
public int hashCode() { return this.id; }
}
まとも
正しいですね!
項目 10 GetHashCode() の罠に注意する P.56
こんな内容のことが書いています
ハッシュコンテナのキーとして使用されることになる値型を作成する場合、その型は不変型にすべき。
??
http://www.amazon.co.jp/Effective-C-%E3%83%93%E3%83%AB-%E3%83%AF%E3%82%B0%E3%83%8A%E3%83%BC/dp/4798119539
まとも x = new まとも ();
x.id = 1;
HashMap< まとも , String> map =
new HashMap< まとも , String>();
map.put(x, "A");
System.out.println(map.get(x)); // A
x.id = 3;
System.out.println(map.get(x)); // null
HashMap で利用すると…
まとめ
Hash の KEY として利用するなら、少なくとも hashCode に利用するフィールドは、 Immutable( 不変 ) が良さそう。
って、中々 Hash の KEY には使わないよね…。