web-dev-qa-db-ja.com

Objects.hash()vs Objects.hashCode()、説明が必要

in Java 7があります

_o.hashCode();
Objects.hashCode(o);

    Objects.hash(o);
_

最初の2つはヌルポイントチェックとほぼ同じですが、最後の2つは何ですか?

単一のオブジェクト参照が提供される場合、戻り値はそのオブジェクト参照のハッシュコードと等しくなりません。

何故ですか?つまり、同じことを行う3つのメソッドは必要ありませんが、それは理解できますが、なぜObjects.hash()が必要なのでしょうか?いつ使用するのを選択しましたか?

51
JAM

hashCode および hash のドキュメントを参照してください。 hashは_Object..._を取り、hashCodeObjectを取ります。例は次のとおりです。

_@Override public int hashCode() {
    return Objects.hash(x, y, z);
}
_
  • Objects.hash(Object... values)は、オブジェクトのシーケンスのハッシュが必要な場合に使用する必要があります。独自のhashCodeメソッドを定義し、オブジェクトのIDを構成する複数の値に対して単純にコード化されたハッシュが必要な場合。
  • Objects.hashCode(Object o)は、オブジェクトがnullの場合にスローせずに、単一のオブジェクトのハッシュが必要な場合に使用する必要があります。
  • Object::hashCode()は、単一のオブジェクトのハッシュが必要な場合に使用する必要があり、オブジェクトがnullの場合は例外をスローします。

hash(o)hashCode(o)は必ずしも同じものを返さないことに注意してください!単一のオブジェクトに対して実行する場合は、おそらくhashCodeを使用する必要があります。

73
Tim S.

_Objects.hashCode_

ユーティリティメソッド Objects.hashCode( Object o ) は、渡されたオブジェクトでhashCodeメソッドを呼び出すだけです。

NULLを許容

では、なぜこの方法を発明または使用するのでしょうか?オブジェクトのhashCodeメソッドを自分で呼び出すだけではどうですか?

この方法には1つの利点があります:NULL➙_0_。ユーティリティメソッドはnullを許容します。

  • myObjectNULLであるObjects.hashCode( myObject )を呼び出すと、ゼロ(0)が返されます。
  • 対照的に、myObjectNULLであるときにmyObject.hashCode()を呼び出すと、NullPointerException引数がスローされます。

ヌルを許容することが望ましいかどうかは、特定の状況でのあなた自身の判断に依存します。

_Objects.hash_

ユーティリティメソッド Objects.hash( Object o , … ) は、別の目的を果たします。この方法には2つのフェーズがあります。

  • 渡された各オブジェクトで_.hashCode_を呼び出し、各結果を収集します。
  • 収集された結果のハッシュを計算します。

ハッシュのハッシュ

単一のオブジェクトObjects.hash( myObject )を渡すと、最初に_myObject.hashCode_が呼び出されて収集され、次にその単一項目コレクションのハッシュが計算されます。したがって、ハッシュの hash になります。

単一のオブジェクトをハッシュする場合、Objects.hashCode( myObject )Objects.hash( myObject )とは異なる結果を返すことを理解することが重要です。事実上、2番目は最初の結果のハッシュを返します。

実際には迷惑

これらの2つのObjectsメソッドで採用されているアプローチのロジックは、それ自体が理にかなっています。

残念ながら、実際には、POJOでhashCodeおよびそれに対応するequalsをオーバーライドするコードを記述するときに日常的に使用しようとする人にとっては、決定するために2度考えなければなりません。どちらを呼び出すか。

  • hashCode(およびequals)オーバーライドがクラスの単一のメンバーに基づいている場合は、Objects.hashCode( member )を使用します。
  • hashCode(およびequals)オーバーライドがクラスの複数の属性に基づいている場合は、Objects.hash( memberA , memberB , memberC )を使用します。

NULLを許容しない単一のメンバー

_@Override
public int hashCode() {
    return this.member.hashCode() ;  // Throws NullPointerException if member variable is null.
}
_

NULLを許容する単一のメンバー

_@Override
public int hashCode() {
    return Objects.hashCode( this.member ) ;  // Returns zero (0) if `this.member` is NULL, rather than throwing exception.
}
_

NULLを許容するマルチメンバー

_@Override
public int hashCode() {
    return Objects.hash( this.memberA , this.memberB , this.memberC  ) ;  // Hashes the result of all the passed objects’ individual hash codes.  
}
_

これらのさまざまな方法を非常に簡単にテストできます。

UUID

例として UUID オブジェクトを使用してみましょう。 [〜#〜] uuid [〜#〜]普遍的に一意の識別子)は128ビット値であり、特定の場合ビットには特定のセマンティクスがあります。

UUIDOpenJDK実装は、128ビット値を64ビットlong整数のペアとして内部的に表します。 。

同じ実装は_Object::equals_および_Object::hashCode_をオーバーライドして、その長い整数のペアに格納されているデータを調べます。 これら2つのメソッドのソースコード です。

_public boolean equals(Object obj) {
    if ((null == obj) || (obj.getClass() != UUID.class))
        return false;
    UUID id = (UUID)obj;
    return (mostSigBits == id.mostSigBits &&
            leastSigBits == id.leastSigBits);
}
_
_public int hashCode() {
    long hilo = mostSigBits ^ leastSigBits;
    return ((int)(hilo >> 32)) ^ (int) hilo;
}
_

サンプルコード

UUIDオブジェクトをインスタンス化します。

_UUID uuid = UUID.randomUUID();
_

ハッシュ値を計算します。

_int hash1 = uuid.hashCode();
int hash2 = Objects.hashCode( uuid );  // Result matches line above.

int hash3 = Objects.hash( uuid );  // Returns a hash of a hash.
int hash4 = Objects.hash( uuid.hashCode() ); // Result matches line above.
_

コンソールにダンプします。

_System.out.println( "uuid.toString(): " + uuid.toString() );
System.out.println( " 1/2 = " + hash1 + " | " + hash2 );
System.out.println( " 3/4 = " + hash3 + " | " + hash4 );
_

こちらをご覧ください コードはIdeOne.comでライブ実行

uuid.toString():401d88ff-c75d-4607-bb89-1f7a2c6963e1

1/2 = 278966883 | 278966883

3/4 = 278966914 | 278966914

13
Basil Bourque

ObjectのデフォルトのhashCode()は、オブジェクトのメモリアドレスを返します。したがって、次のクラスがある場合:

class Car {
    String make;
    String model;
    int year;

    public Car(String make, String model, int year) {
        this.make = make;
        this.model = model;
        this.year = year;
    }
} 

次に、2つのオブジェクトを作成します。

Car car1 = new Car("Toyota", "Corolla", 2010);
Car car2 = new Car("Toyota", "Corolla", 2010);

car1.hashCode()はcar2.hashCode()とは異なります。これは、各オブジェクトが異なるメモリアドレスを持つためです。

Car1とcar2の両方が同じハッシュコードを返すようにするにはどうしますか?この場合、次のようにCarクラスのデフォルトのObject hashCode()メソッドをオーバーライドする必要があります。

@Override
public int hashCode() {
    Object[] x = {model, make, Integer.valueOf(year)};
    int hashArray = Arrays.hashCode(x);
    return hashArray;
}

これにより、string.hashCode()は文字列の内容に基づいてhashCodeを計算し、Integer.hashCode()は整数値自体を返すため、car1.hashCode()はcar2.hashCode()と等しくなります。

Java 7では、Objects.hash(Object ... values)を使用できます。したがって、新しいCar hashCode()は次のようになります。

@Override
public int hashCode() {
    return Objects.hash(model, make, year);
}

Objects.hash(Object ... values)は、Arrays.hashCodeを呼び出します。

最後に、Objects.hashCode(Object o)はnullチェックを行います。オブジェクトがnullの場合、0を返します。そうでない場合は、オブジェクトのhashCode()メソッドを呼び出します。

11
ezzadeen