8

Joshua Bloch による効果的な Java の次のコードがあります (項目 9、第 3 章、49 ページ)。

クラスが不変で、ハッシュ コードの計算コストが大きい場合は、要求されるたびに再計算するのではなく、オブジェクトにハッシュ コードをキャッシュすることを検討してください。このタイプのほとんどのオブジェクトがハッシュ キーとして使用されると思われる場合は、インスタンスの作成時にハッシュ コードを計算する必要があります。それ以外の場合は、最初に hashCode が呼び出されたときに遅延初期化することを選択できます (項目 71)。PhoneNumber クラスがこの処理に値するかどうかは明らかではありませんが、それがどのように行われるかを示すだけです:

    // Lazily initialized, cached hashCode
    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;
    }

私の質問は、キャッシング (hashCode を記憶) がここでどのように機能するかです。初めてhashCode()メソッドが呼び出されたとき、hashCodeそれを結果に割り当てる必要はありません。このキャッシングがどのように機能するかについての簡単な説明は素晴らしいでしょう。ありがとう

4

3 に答える 3

12

単純。以下の私の埋め込みコメントを読んでください...

private volatile int hashCode;
//You keep a member field on the class, which represents the cached hashCode value

   @Override public int hashCode() {
       int result = hashCode;
       //if result == 0, the hashCode has not been computed yet, so compute it
       if (result == 0) {
           result = 17;
           result = 31 * result + areaCode;
           result = 31 * result + prefix;
           result = 31 * result + lineNumber;
           //remember the value you computed in the hashCode member field
           hashCode = result;
       }
       // when you return result, you've either just come from the body of the above
       // if statement, in which case you JUST calculated the value -- or -- you've
       // skipped the if statement in which case you've calculated it in a prior
       // invocation of hashCode, and you're returning the cached value.
       return result;
   }
于 2013-08-27T18:51:24.043 に答える
2

hashCodeインスタンス変数内の変数であり、明示的に初期化されていないため、Java はそれを0(JLS セクション 4.12.5)に初期化します。この比較result == 0は、実際にはresult、おそらくゼロ以外のハッシュ コードが割り当てられているかどうかを確認するためのチェックです。まだ割り当てられていない場合は計算を実行し、それ以外の場合は以前に計算されたハッシュ コードを返します。

于 2013-08-27T18:41:55.240 に答える
-1

これを正しく機能させたい場合は、isHashInvalid という別の揮発性変数ブール値を配置します。ハッシュ関数でアクセスされる値を含むすべてのセッターは、この変数を設定します。次に、次のようになります (「0」をテストする必要はありません):

private volatile int isHashInvalid=TRUE;
private volatile int hashCode; //Automatically zero but it doesn't matter

//You keep a member field on the class, which represents the cached hashCode value
@Override public int hashCode() {
    int result = hashCode;
    if (isHashInvalid) {
       result = 17;
       result = 31 * result + areaCode;
       result = 31 * result + prefix;
       result = 31 * result + lineNumber;
       //remember the value you computed in the hashCode member field
       hashCode = result;
       isHashInvalid=FALSE;
    }
    // when you return result, you've either just come from the body of the above
    // if statement, in which case you JUST calculated the value -- or -- you've
    // skipped the if statement in which case you've calculated it in a prior
    // invocation of hashCode, and you're returning the cached value.
    return result;
}
于 2013-09-29T23:57:37.120 に答える