6

equalsメソッドがJavaでオーバーライドされるたびに、ハッシュコードをオーバーライドする必要があることを私は知っています。それは単なる契約です。この背後にあるロジックを理解しようとしています。*Effective Java by Joshua Blochを読んでいて、このコードに出くわしました (項目 9、45 ページ):

import java.util.HashMap;
import java.util.Map;

public final class PhoneNumber {
    private final short areaCode;
    private final short prefix;
    private final short lineNumber;

    public PhoneNumber(int areaCode, int prefix, int lineNumber) {
        rangeCheck(areaCode, 999, "area code");
        rangeCheck(prefix, 999, "prefix");
        rangeCheck(lineNumber, 9999, "line number");
        this.areaCode = (short) areaCode;
        this.prefix = (short) prefix;
        this.lineNumber = (short) lineNumber;
    }

    private static void rangeCheck(int arg, int max, String name) {
        if (arg < 0 || arg > max)
            throw new IllegalArgumentException(name + ": " + arg);
    }

    @Override
    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof PhoneNumber))
            return false;
        PhoneNumber pn = (PhoneNumber) o;
        return pn.lineNumber == lineNumber && pn.prefix == prefix
                && pn.areaCode == areaCode;
    }

    // Broken - no hashCode method!

    // A decent hashCode method - Page 48
    // @Override public int hashCode() {
    // int result = 17;
    // result = 31 * result + areaCode;
    // result = 31 * result + prefix;
    // result = 31 * result + lineNumber;
    // return result;
    // }

    // Lazily initialized, cached hashCode - Page 49
    // private volatile int hashCode; // (See Item 71)
    //
    // @Override public int hashCode() {
    // int result = hashCode;
    // if (result == 0) {
    // result = 17;
    // result = 31 * result + areaCode;
    // result = 31 * result + prefix;
    // result = 31 * result + lineNumber;
    // hashCode = result;
    // }
    // return result;
    // }

    public static void main(String[] args) {
        Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>();
        m.put(new PhoneNumber(707, 867, 5309), "Jenny");
        System.out.println(m.get(new PhoneNumber(707, 867, 5309)));
    }
}

これは彼がテキストで言及していることですが、私には理解が困難です。

この時点で、m.get(new PhoneNumber(707, 867, 5309))"Jenny" が返されることを期待するかもしれませんが、null が返されます。2 つの PhoneNumber インスタンスが関係していることに注意してください。PhoneNumber クラスが hashCode のオーバーライドに失敗すると、ハッシュコード コントラクトに違反して、2 つの等しいインスタンスのハッシュコードが等しくなくなります。したがって、get メソッドは、put メソッドによって格納されたハッシュ バケットとは別のハッシュ バケットで電話番号を検索する可能性があります。

彼が話している 2 つの PhoneNumber インスタンスの意味がわかりません。で作成したインスタンスのみがありますm.put(new PhoneNumber(707, 867, 5309), "Jenny")。また、オブジェクト クラスから hashCode メソッドを継承している場合でも、同じハッシュコードを返すはずのこのオブジェクトをもう一度探します。なぜこれが起こるのですか?ここでの説明は大いに役立ちます。

4

6 に答える 6

4

彼が話している 2 つの PhoneNumber インスタンスについて理解できません。

最初のものは、挿入に使用したものです。

m.put(new PhoneNumber(707, 867, 5309), "Jenny"));

2 番目のインスタンスは、取得に使用されるものです。

m.get(new PhoneNumber(707, 867, 5309)); // Notice the use of "new"

また、オブジェクト クラスから hashCode メソッドを継承している場合でも、同じハッシュコードを返すはずのこのオブジェクトをもう一度探します。

それは正しくありません。hashCode()inクラスのデフォルトの実装はObject、オブジェクトの内部アドレスを整数に変換することによって実装されるため、個別のオブジェクトに対して個別の整数を返します。したがって、ハッシュコードチェックはそこで失敗します。

一方、PhoneNumber同じインスタンスを使用してを取得しようとした場合

PhoneNumber phoneNum = new PhoneNumber(707, 867, 5309);
m.put(phoneNum, "Jenny"));
m.get(phoneNum); // Not NULL

ハッシュコードチェックはパスします (以前に挿入されたのと同じオブジェクトを使用したため) equals()。もちろん、挿入に使用したものとは異なるキー オブジェクトを使用する可能性がはるかに高いため、これは推奨されるアプローチではありません。

ただし、使用されるキーは意味のある同等のもの (別の String オブジェクトのように、テキストは同じ) であるhashCode()ため、一致と取得を正しく行うには実装を提供する必要があります。

参照: Java HashMap の要素の確認と削除

于 2013-08-24T05:08:53.257 に答える
3

hashcode一緒にオーバーライドしないとequals、すべてのインスタンス、たとえば "new PhoneNumber(707, 867, 5309)"は異なるハッシュコードを持つことになります。

したがって、HashMap の観点からは、これらは 2 つの異なるエントリとして扱われます。ハッシュマップの仕組みについてもっと読んでください。したがって、2 つのオブジェクトが等しい可能性があるが、hascode が異なる場合、異なるバケットに格納されます。

于 2013-08-24T05:08:38.250 に答える
0

の目的はhashCode()、オブジェクトが等しくないものをすばやく識別することです。を 1 回呼び出すと、そのオブジェクトが、 hasメソッドが呼び出されて別の値を返したhashCode()オブジェクトと等しくないことが即座に明らかになります。これは非常に強力な機能ですが、「等しい」2 つのオブジェクトは同じ値hashCode()を返す必要があります。hashCode2 つのオブジェクトが同じ値を返さないhashCode場合、一部のコレクション型は、それらが等しい可能性はないと見なし、それらが等しいequalsかどうかを確認するためにわざわざ呼び出しません。

于 2013-10-05T00:01:00.130 に答える
0

このリンクに移動

ハッシュコードは、コントラクトを維持し、ハッシュマップまたはハッシュテーブル内の各オブジェクトを一意に識別するために使用されます。

于 2013-08-24T05:06:17.960 に答える
0

あなたの質問のために。

また、オブジェクト クラスから hashCode メソッドを継承している場合でも、同じハッシュコードを返すはずのこのオブジェクトをもう一度探します。

こちらの Object#hashCode ドキュメントを確認してください

合理的に実用的である限り、the hashCode method defined by class Object does return distinct integers for distinct objects. (これは通常、オブジェクトの内部アドレスを整数に変換することによって実装されますが、この実装手法は JavaTM プログラミング言語では必要ありません。)

つまり、hashCode上書きしない限り、2 つのオブジェクトに対して同じ整数を返すことはありません。

于 2013-08-24T09:47:56.713 に答える
0

インスタンスが 1 つしかないと思っているのに、インスタンスが2 つある。各

新しい電話番号 (707、867、5309)

別の記憶場所にインスタンスを作成します。ハッシュ マップ メソッドm.getは、メソッド呼び出しで作成する新しいインスタンスを探しています。このインスタンスには、m.putメソッドで最初に作成されたインスタンスへの別のハッシュ コードがあります。

(2 つのオブジェクト インスタンスがあるため、Java はスーパー クラス オブジェクトで異なる hashCode を計算します。)

したがって、ハッシュ マップは、2 番目のオブジェクトの hashCode を持つ最初のオブジェクトを見つけることができませんでした。

電話番号を変数に格納し、変数を使用して入れたり取得したりしてください。これは、同じ hashCode と equals == true を持つ同じメモリ位置にある同じオブジェクトであるためです。

public static void main(String[] args) {
    Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>();
    PhoneNumber num = new PhoneNumber(707, 867, 5309);
    m.put(num, "Jenny");
    System.out.println(m.get(num));
}

hashCodeしかし、実際に使用するには、およびメソッドを正しく実装する必要がありequalsます。

于 2013-08-24T20:35:15.530 に答える