5

ここでは、1 つのサンプル コードを記述しています。

public class Test {

    private int i;
    private int j;

    public Test() {
        // TODO Auto-generated constructor stub
    }

    public Test(int i, int j)
    {
        this.i=i;
        this.j=j;
    }
}

今、次のように 2 つのオブジェクトを作成しています。

Test t1= new Test(4,5);
Test t2 = new Test(4,5);

しかし、私が t1.hashcode() と t2.hashcode() を印刷しているとき、それらは異なる値を与えています。しかし、Java の一般的な連絡先によると、同じ値を返す必要があります。実際、私が String または Integer で同じことをしているとき、それらは同じ hashcode() を返しています。t1 オブジェクトと t2 オブジェクトでハッシュコードが異なる理由を誰か説明してもらえますか?

4

4 に答える 4

20

しかし、Java の一般的な連絡先によると、同じ値を返す必要があります。

Java のequals-hashCode契約では、2 つのオブジェクトが で等しい場合Object.equals、それらは からの同じハッシュコードを持っている必要がありますObject.hashCode。ただし、デフォルトの実装Object.equals参照等価であるため、2 つのインスタンスが同じである場合は、それらが同じインスタンスである場合に限ります。

したがって、特に 2 つのインスタンスt1t2は、オーバーライドしていないため、実際に等しくありませんObject.equals。これらは参照として等しくないため、 ごとObject.equalsに等しくないため、 がhashCode異なる値を返す可能性があります。実際、契約には次のように明示的に記載されています。

2 つのオブジェクトがメソッドによって等しくない場合、2 つのオブジェクトのそれぞれでメソッドequals(java.lang.Object)を呼び出すと、hashCode異なる整数結果が生成される必要はありません。

equals-hashCodeしたがって、ここでは契約違反はありません。

したがって、オブジェクトについて、等価性の論理定義に従って異なるインスタンスを等しくしたい場合は、オーバーライドする必要がありますObject.equals

 @Override
 public boolean equals(Object obj) {
     if (obj == null) {
         return false;
     if (this == obj) {
          return true;
     }
     if (!(obj instanceof Test)) {
          return false;
     }
     Test other = (Test)obj; 
     return this.i == other.i && this.j == other.j;
 }

また、equals-hashCode契約ではオーバーライドObject.hashCodeも必要です。そうしないと、厄介なバグに遭遇します。

 @Override
 public int hashCode() {
     int hash = 17; 
     hash = 31 * hash + this.i;
     hash = 31 * hash + this.j;
     return hash;
 }

契約書の内容:

メソッドに従って 2 つのオブジェクトが等しい場合、2 つのオブジェクトのそれぞれでメソッドequals(Object)を呼び出すとhashCode、同じ整数の結果が生成される必要があります。

ここで、この要件を満たしているかどうかを見てみましょう。xyが のインスタンスであり、 がTestを満たす場合、 とがあります。次に、明らかに、呼び出して、実行の各行で同じ値を保持する不変式がある場合。どちらの場合もそうであるため、明らかにこれは最初の行に当てはまります。が等しいかどうかにかかわらず同じ値を返すため、2 行目に保持されます。最後に、最後から 2 番目の行では、equalsも true であるため、両方の呼び出しで等しいままです。x.equals(y)truex.i == y.ix.j == y.jx.hashCode()y.hashCode()Test.hashCodehashhash17this.ithis == xthis == yx.iy.ihashx.jy.j

まだ説明していないコントラクトの最後の部分があることに注意してください。hashCodeこれは、Java アプリケーションの 1 回の実行中に一貫した値を返すという要件です。

hashCodeオブジェクトの equals 比較で使用される情報が変更されていない限り、メソッドは、Java アプリケーションの実行中に同じオブジェクトに対して複数回呼び出されるたびに、一貫して同じ整数を返す必要があります。

これの必要性は明らかです。hashCode同じアプリケーションの 1 回の実行中に戻り値を変更するとhashCode、オブジェクトの追跡に使用されるハッシュテーブルのようなデータ構造でオブジェクトが失われる可能性があります。特に、これが、ハッシュテーブルのようなデータ構造のキーであるオブジェクトを変更することが純粋な悪である理由です。しないでください。私は、それらが不変オブジェクトであるべきだと主張するところまで行きます。

実際、私が同じことをしているとき、StringまたはInteger彼らが同じを返しているときhashcode()

それらは両方ともオーバーライドさObject.equalsれており、Object.hashCode.

于 2013-06-18T03:02:33.680 に答える
3

クラスのメソッドをオーバーライドしていないequalsため、クラスに属するデフォルトのメソッドが使用されますObject。オブジェクト クラス メソッドは、参照が同じオブジェクトを参照しているかどうかを単純にチェックします。

Test t1 = new Test(4,5);
Test t2 = new Test(4,5);

は 2 つの異なるオブジェクトです。ここでメソッドをオーバーライドしないと、オーバーライドしたequals場合にのみ等しくなります。

Test t2 = t1;

objectここで 2 つの異なるオブジェクトを作成しているため、同じを参照していないため等しくないハッシュコードは異なるhashcodes必要があります。覚えておいてください。

  • 2 つのオブジェクトが等しい場合、それらのハッシュコードMUSTは等しい
  • しかし、ハッシュコードが等しい場合、オブジェクトが等しい必要はありません
于 2013-06-18T03:07:11.877 に答える
1

これは、Javaでのequalsおよびのデフォルトの実装によるものです。hashCode

JVM には、ユーザーが 2 つのオブジェクトが同じであると判断する方法を知る方法がありません。それが行うことは、メモリ参照を使用することです。したがって、デフォルトでは、 メソッドequalsとメソッドはメモリ参照hashCodeを比較します。つまり、2 つの異なるオブジェクトは決して. .equals

Collectionこの動作をオーバーライドしたい場合 (たとえば sを使用したい場合はオーバーライドすることをお勧めします)、独自のequalsandhashCodeメソッドを実装するだけで済みます。

于 2013-06-18T03:03:17.917 に答える