equals()
(Javadoc)等価関係を定義しなければならない(それは反射的、左右対称、そして推移的)さらに、それはする必要があります一貫している(オブジェクトが変更されていない場合は、同じ値を返し続ける必要があります)。さらに、o.equals(null)
常にfalseを返す必要があります。
hashCode()
(Javadocもする必要があります一貫している(オブジェクトが以下の点で変更されていない場合equals()
同じ値を返し続ける必要があります。
の関係2つの方法の間にあります:
いつでも
a.equals(b)
それからa.hashCode()
と同じでなければなりませんb.hashCode()
。
一方を上書きする場合は、もう一方を上書きする必要があります。
計算に使用したものと同じ一連のフィールドを使用するequals()
計算しますhashCode()
。
優れたヘルパークラスを使うEqualsBuilderそしてHashCodeBuilderからApache Commons Langとしょうかん。例:
public class Person {
private String name;
private int age;
// ...
@Override
public int hashCode() {
return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers
// if deriving: appendSuper(super.hashCode()).
append(name).
append(age).
toHashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Person))
return false;
if (obj == this)
return true;
Person rhs = (Person) obj;
return new EqualsBuilder().
// if deriving: appendSuper(super.equals(obj)).
append(name, rhs.name).
append(age, rhs.age).
isEquals();
}
}
ハッシュベースを使用している場合コレクションまたは地図といったハッシュセット、LinkedHashSet、ハッシュマップ、ハッシュ表または弱ハッシュマップオブジェクトがコレクション内にある間は、コレクションに入れるキーオブジェクトのhashCode()が変更されないようにしてください。これを確実にするための防弾方法はあなたの鍵を不変にすることです。他にも利点があります。
HibernateのようなObject-Relationship Mapper(ORM)を使って永続化されているクラスを扱っているのであれば、注目に値する問題がいくつかあります。
遅延ロードされたオブジェクトはサブクラスです
オブジェクトがORMを使用して永続化されている場合は、多くの場合、データストアからオブジェクトを早くロードし過ぎないように動的プロキシを使用します。これらのプロキシはあなた自身のクラスのサブクラスとして実装されています。この意味はthis.getClass() == o.getClass()
戻りますfalse
。例えば:
Person saved = new Person("John Doe");
Long key = dao.save(saved);
dao.flush();
Person retrieved = dao.retrieve(key);
saved.getClass().equals(retrieved.getClass()); // Will return false if Person is loaded lazy
ORMを扱っているのなら、o instanceof Person
正しく動作するのはこれだけです。
遅延ロードされたオブジェクトはnullフィールドを持ちます
ORMは通常、ゲッターを使用して遅延ロードされたオブジェクトを強制的にロードします。この意味はperson.name
になりますnull
もしperson
たとえ遅延ロードされていてもperson.getName()
ロードを強制し、 "John Doe"を返します。私の経験では、これはhashCode()
そしてequals()
。
もしあなたがORMを扱っているのなら、必ずゲッターを使うようにしてください。hashCode()
そしてequals()
。
オブジェクトを保存するとその状態が変わります
永続オブジェクトはしばしばid
オブジェクトのキーを保持するフィールド。このフィールドは、オブジェクトが最初に保存されたときに自動的に更新されます。でidフィールドを使用しないhashCode()
。しかし、あなたはそれを使用することができますequals()
。
私がよく使うパターンは
if (this.getId() == null) {
return this == other;
}
else {
return this.getId().equals(other.getId());
}
しかし:あなたは含めることはできませんgetId()
にhashCode()
。そうすると、オブジェクトが永続化されるときに、hashCode
変更します。オブジェクトがHashSet
二度とそれを見つけることは決してないだろう。
私の中でPerson
例えば、私はおそらく使用しますgetName()
にとってhashCode
そしてgetId()
もっとgetName()
(パラノイア用)equals()
。 「衝突」の危険性がある場合は問題ありません。hashCode()
、でも大丈夫equals()
。
hashCode()
から変更しないプロパティのサブセットを使用する必要があります。equals()
Saving an object will change it's state
!hashCode
帰らなければならないint
だから、あなたはどのように使うのですかgetName()
?あなたの例を挙げてもらえますかhashCode
- jimmybondy
についての説明obj.getClass() != getClass()
。
この文はequals()
不親切な相続JLS(Java言語仕様)では、次のように指定されています。A.equals(B) == true
それからB.equals(A)
また帰らなければならないtrue
。その文を省略した場合は、オーバーライドするクラスを継承します。equals()
(そしてその振る舞いを変更する)この仕様を破るでしょう。
次のステートメントが省略されたときの動作の例を考えてください。
class A {
int field1;
A(int field1) {
this.field1 = field1;
}
public boolean equals(Object other) {
return (other != null && other instanceof A && ((A) other).field1 == field1);
}
}
class B extends A {
int field2;
B(int field1, int field2) {
super(field1);
this.field2 = field2;
}
public boolean equals(Object other) {
return (other != null && other instanceof B && ((B)other).field2 == field2 && super.equals(other));
}
}
やるnew A(1).equals(new A(1))
また、new B(1,1).equals(new B(1,1))
それが本来あるべきように、結果は真実を出します。
これはすべて非常に良さそうに見えますが、両方のクラスを使用しようとするとどうなるかを調べます。
A a = new A(1);
B b = new B(1,1);
a.equals(b) == true;
b.equals(a) == false;
明らかに、これは間違っています。
対称条件を確保したい場合b = aでLiskovの代入原理が呼び出す場合、a = bsuper.equals(other)
の場合だけでなくB
インスタンスですが、後で確認してくださいA
インスタンス:
if (other instanceof B )
return (other != null && ((B)other).field2 == field2 && super.equals(other));
if (other instanceof A) return super.equals(other);
else return false;
どれが出力されます:
a.equals(b) == true;
b.equals(a) == true;
どこでa
の参照ではありませんB
それから、それはクラスの参照であるかもしれませんA
(あなたがそれを拡張しているので)、この場合あなたは呼ぶsuper.equals()
も。
ThingWithOptionSetA
aに等しいThing
すべての追加オプションにデフォルト値があり、同様にThingWithOptionSetB
それからそれは可能であるべきですThingWithOptionSetA
aと等しいThingWithOptionSetB
両方のオブジェクトの基本以外のすべてのプロパティがデフォルトと一致する場合に限りますが、そのテスト方法がわかりません。 - supercatB b2 = new B(1,99)
それからb.equals(a) == true
そしてa.equals(b2) == true
しかしb.equals(b2) == false
。 - nickgrim
継承に適した実装については、Tal Cohenのソリューションを調べてください。equals()メソッドを正しく実装する方法
概要:
彼の本の中で効果的なJavaプログラミング言語ガイド(Addison-Wesley、2001)、Joshua Blochは、「インスタンス化可能なクラスを拡張し、同等の契約を維持しながらアスペクトを追加する方法はまったくない」と主張している。タルは同意しません。
彼の解決策は、別の非対称blindlyEquals()を両方向に呼び出すことによってequals()を実装することです。 blindlyEquals()はサブクラスによってオーバーライドされ、equals()は継承され、決してオーバーライドされません。
例:
class Point {
private int x;
private int y;
protected boolean blindlyEquals(Object o) {
if (!(o instanceof Point))
return false;
Point p = (Point)o;
return (p.x == this.x && p.y == this.y);
}
public boolean equals(Object o) {
return (this.blindlyEquals(o) && o.blindlyEquals(this));
}
}
class ColorPoint extends Point {
private Color c;
protected boolean blindlyEquals(Object o) {
if (!(o instanceof ColorPoint))
return false;
ColorPoint cp = (ColorPoint)o;
return (super.blindlyEquals(cp) &&
cp.color == this.color);
}
}
次の場合、equals()は継承階層全体で機能する必要があります。リスコフ代用原則満足することです。
if (this.getClass() != o.getClass()) return false
しかし、派生クラスが修正を煩わす場合にのみfalseを返すという点で柔軟です。その通りですか? - Aleksandr Dubinsky
まだ誰もこれのためにguavaライブラリを推薦しなかったことを驚かせた。
//Sample taken from a current working project of mine just to illustrate the idea
@Override
public int hashCode(){
return Objects.hashCode(this.getDate(), this.datePattern);
}
@Override
public boolean equals(Object obj){
if ( ! obj instanceof DateAndPattern ) {
return false;
}
return Objects.equal(((DateAndPattern)obj).getDate(), this.getDate())
&& Objects.equal(((DateAndPattern)obj).getDate(), this.getDatePattern());
}
this
にthis.getDate()
何も意味しません(雑然以外) - Steve Kuoif (!(otherObject instanceof DateAndPattern)) {
。 hernanとSteve Kuoに同意します(ただし、個人的な好みの問題ですが)。ただし+1です。 - Amos M. Carpenter
スーパークラスには、java.lang.Objectという2つのメソッドがあります。それらをカスタムオブジェクトにオーバーライドする必要があります。
public boolean equals(Object obj)
public int hashCode()
等しいオブジェクトは等しい限り同じハッシュコードを生成しなければなりませんが、異なるオブジェクトは異なるハッシュコードを生成する必要はありません。
public class Test
{
private int num;
private String data;
public boolean equals(Object obj)
{
if(this == obj)
return true;
if((obj == null) || (obj.getClass() != this.getClass()))
return false;
// object must be Test at this point
Test test = (Test)obj;
return num == test.num &&
(data == test.data || (data != null && data.equals(test.data)));
}
public int hashCode()
{
int hash = 7;
hash = 31 * hash + num;
hash = 31 * hash + (null == data ? 0 : data.hashCode());
return hash;
}
// other methods
}
もっと知りたい場合は、このリンクをチェックしてください。http://www.javaranch.com/journal/2002/10/equalhash.html
これは別の例です。http://java67.blogspot.com/2013/04/example-of-overriding-equals-hashcode-compareTo-java-method.html
楽しむ! @。
メンバーの平等をチェックする前にクラスの平等をチェックする方法はいくつかありますが、どちらも正しい状況では便利だと思います。
instanceof
オペレーター。this.getClass().equals(that.getClass())
。私は#1をfinal
equals実装、またはequalsのアルゴリズムを規定するインタフェースを実装するときjava.util
コレクションのインターフェース - 確認する正しい方法(obj instanceof Set)
あるいは、あなたが実装しているどんなインターフェースでも。これは、対称性を損なうため、equalsをオーバーライドできる場合は一般に悪い選択です。
オプション#2を指定すると、等号をオーバーライドしたり対称性を破ったりせずにクラスを安全に拡張できます。
あなたのクラスもComparable
、equals
そしてcompareTo
方法も一貫しているべきです。これは、のequalsメソッドのテンプレートです。Comparable
クラス:
final class MyClass implements Comparable<MyClass>
{
…
@Override
public boolean equals(Object obj)
{
/* If compareTo and equals aren't final, we should check with getClass instead. */
if (!(obj instanceof MyClass))
return false;
return compareTo((MyClass) obj) == 0;
}
}
final
、 そしてそのcompareTo()
ソート順を逆にするためにmethodがオーバーライドされました。サブクラスとスーパークラスのインスタンスは等しいと見なすべきではありません。これらのオブジェクトがツリー内で一緒に使用されていた場合、「等しい」キーが使用されていました。によるとinstanceof
実装が見つからない可能性があります。 - erickson
等しい場合は、平等の秘密によってアンジェリカランガー。私はそれが大好きです。彼女はまたについての素晴らしいFAQです。Javaのジェネリックス。彼女の他の記事を見るここに( "Core Java"までスクロールしてください)そこで彼女はまたPart-2と "混合型比較"を続けます。それらを読んで楽しんでください!
equals()メソッドは、2つのオブジェクトが等しいかどうかを判断するために使用されます。
10のint値は常に10に等しいので。しかし、このequals()メソッドは2つのオブジェクトの同等性についてです。我々がオブジェクトを言うとき、それはプロパティを持ちます。平等について決定するために、それらの特性が考慮されます。同等性を決定するために、そしてクラス定義とそれが決定されることができる文脈に関して、すべての特性を考慮に入れなければならない必要はない。その後、equals()メソッドをオーバーライドできます。
equals()メソッドをオーバーライドするたびに、常にhashCode()メソッドをオーバーライドする必要があります。そうでなければ、どうなりますか?アプリケーションでハッシュテーブルを使用すると、期待通りに動作しません。 hashCodeは格納されている値が等しいかどうかを判断するために使用されるため、キーに対応する正しい値を返しません。
与えられたデフォルト実装はObjectクラスのhashCode()メソッドで、オブジェクトの内部アドレスを使ってそれを整数に変換して返します。
public class Tiger {
private String color;
private String stripePattern;
private int height;
@Override
public boolean equals(Object object) {
boolean result = false;
if (object == null || object.getClass() != getClass()) {
result = false;
} else {
Tiger tiger = (Tiger) object;
if (this.color == tiger.getColor()
&& this.stripePattern == tiger.getStripePattern()) {
result = true;
}
}
return result;
}
// just omitted null checks
@Override
public int hashCode() {
int hash = 3;
hash = 7 * hash + this.color.hashCode();
hash = 7 * hash + this.stripePattern.hashCode();
return hash;
}
public static void main(String args[]) {
Tiger bengalTiger1 = new Tiger("Yellow", "Dense", 3);
Tiger bengalTiger2 = new Tiger("Yellow", "Dense", 2);
Tiger siberianTiger = new Tiger("White", "Sparse", 4);
System.out.println("bengalTiger1 and bengalTiger2: "
+ bengalTiger1.equals(bengalTiger2));
System.out.println("bengalTiger1 and siberianTiger: "
+ bengalTiger1.equals(siberianTiger));
System.out.println("bengalTiger1 hashCode: " + bengalTiger1.hashCode());
System.out.println("bengalTiger2 hashCode: " + bengalTiger2.hashCode());
System.out.println("siberianTiger hashCode: "
+ siberianTiger.hashCode());
}
public String getColor() {
return color;
}
public String getStripePattern() {
return stripePattern;
}
public Tiger(String color, String stripePattern, int height) {
this.color = color;
this.stripePattern = stripePattern;
this.height = height;
}
}
コード出力例
bengalTiger1 and bengalTiger2: true
bengalTiger1 and siberianTiger: false
bengalTiger1 hashCode: 1398212510
bengalTiger2 hashCode: 1398212510
siberianTiger hashCode: –1227465966
論理的には
a.getClass().equals(b.getClass()) && a.equals(b)
⇒a.hashCode() == b.hashCode()
しかしではない逆に!
私が見つけた1つの落とし穴は、2つのオブジェクトがお互いへの参照を含む場合です(1つの例は、すべての子を取得するために親に便利メソッドを持つ親子関係です)。
たとえば、Hibernateマッピングを実行する場合、この種のことはかなり一般的です。
hashCodeまたはequalsテストに関係の両端を含めると、StackOverflowExceptionで終わる再帰ループに入る可能性があります。
最も簡単な解決策は、メソッドにgetChildrenコレクションを含めないことです。
equals()
。気が狂った科学者が私の写しを作成したならば、私たちは同等であろう。しかし、私たちには同じ父親がいないでしょう。 - Raedwald
instanceof
最初のオペランドがNULLの場合はfalseを返します(これも有効なJavaです)。 - izaban