web-dev-qa-db-ja.com

どのハッシュ関数がJava Hashtableクラスの実装に使用しますか?

本CLRS( "Introduction to Algorithms")から、mod、multiplyなどのいくつかのハッシュ関数があります。

キーをスロットにマップするために使用するハッシュ関数は何ですかJava?

私はここに質問があるのを見ました Java Language で使用されるハッシュ関数)。しかし、それは質問に答えません。 hashCode()を使用すると、Hashtableに対して独自のハッシュ関数を実行できますが、間違っていると思います。

HashCode()が返す整数はHashtbleの実際のキーであり、Hashtableはハッシュ関数を使用してhashCode()をハッシュします。この答えが意味するのは、JavaはHashtableにハッシュ関数を与える機会を与えますが、そうではありません。それは間違っています。hashCode()はハッシュ関数ではなく実際のキーを与えます。

それでは、ハッシュ関数は正確に何をJava使用しますか?

49
Jackson Tale

OpenJDKのHashMapにキーが追加または要求されると、実行のフローは次のようになります。

  1. キーは、開発者が定義したhashCode()メソッドを使用して32ビット値に変換されます。
  2. 次に、32ビット値は2番目のハッシュ関数(Andrewの答えにはソースコードが含まれています)によってハッシュテーブル内のオフセットに変換されます。この2番目のハッシュ関数は、HashMapの実装によって提供され、開発者がオーバーライドすることはできません。
  3. ハッシュテーブルの対応するエントリには、リンクリストへの参照またはキーがまだハッシュテーブルに存在しない場合はnullが含まれます。衝突(同じオフセットを持ついくつかのキー)がある場合、キーとその値は、単一リンクリストに単純に収集されます。

ハッシュテーブルのサイズが適切に大きく選択された場合、衝突の数は制限されます。したがって、単一のルックアップは平均して一定の時間しかかかりません。これはexpected constant timeと呼ばれます。ただし、攻撃者がハッシュテーブルに挿入されたキーを制御し、使用中のハッシュアルゴリズムを知っている場合、多くのハッシュコリジョンを引き起こし、線形ルックアップ時間を強制することができます。これが、いくつかのハッシュテーブルの実装が最近変更されて、どのキーが衝突を引き起こすかを攻撃者が予測するのを困難にするランダム要素を含むようになった理由です。

いくつかASCII art

key.hashCode()
     |
     | 32-bit value
     |                              hash table
     V                            +------------+    +----------------------+
HashMap.hash() --+                | reference  | -> | key1 | value1 | null |
                 |                |------------|    +----------------------+
                 | modulo size    | null       |
                 | = offset       |------------|    +---------------------+
                 +--------------> | reference  | -> | key2 | value2 | ref |
                                  |------------|    +---------------------+
                                  |    ....    |                       |
                                                      +----------------+
                                                      V
                                                    +----------------------+
                                                    | key3 | value3 | null |
                                                    +----------------------+
94
Niklas B.

ハッシュマップのソースによると、すべてのhashCodeは次の方法を使用してハッシュされます。

 /**
 * Applies a supplemental hash function to a given hashCode, which
 * defends against poor quality hash functions.  This is critical
 * because HashMap uses power-of-two length hash tables, that
 * otherwise encounter collisions for hashCodes that do not differ
 * in lower bits. Note: Null keys always map to hash 0, thus index 0.
 */
static int hash(int h) {
    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}

すべてのhashCodeが再びハッシュされる理由は、衝突をさらに防ぐためです(上記のコメントを参照)

HashMapはメソッドを使用してハッシュコードのインデックスを決定します(長さは常に2のべき乗であるため、%の代わりに&を使用できます)。

/**
 * Returns index for hash code h.
 */
static int indexFor(int h, int length) {
    return h & (length-1);
}

Putメソッドは次のようになります。

int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);

ハッシュコードの目的は、特定のオブジェクトに一意の整数表現を提供することです。したがって、各値はそのIntegerオブジェクトに固有であるため、IntegerのhashCodeメソッドは単に値を返すだけです。

27
Andrew Liu

ハッシュは一般に2つのステップに分けられます。ハッシュコードb。圧縮する

ステップa。キーに対応する整数が生成されます。これはJavaで変更できます。

ステップb。圧縮技術はJavaによって適用され、ステップa。で返された整数をハッシュマップまたはハッシュテーブルのスロットにマッピングします。この圧縮技術は変更できません。

4
/**
 * Computes key.hashCode() and spreads (XORs) higher bits of hash
 * to lower.  Because the table uses power-of-two masking, sets of
 * hashes that vary only in bits above the current mask will
 * always collide. (Among known examples are sets of Float keys
 * holding consecutive whole numbers in small tables.)  So we
 * apply a transform that spreads the impact of higher bits
 * downward. There is a tradeoff between speed, utility, and
 * quality of bit-spreading. Because many common sets of hashes
 * are already reasonably distributed (so don't benefit from
 * spreading), and because we use trees to handle large sets of
 * collisions in bins, we just XOR some shifted bits in the
 * cheapest possible way to reduce systematic lossage, as well as
 * to incorporate impact of the highest bits that would otherwise
 * never be used in index calculations because of table bounds.
 */
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

これは、JavaのhashMapクラスで使用される最新のハッシュ関数です

1
Avinash

ここでコンセプトについていくらか混乱があると思います。ハッシュ関数は、可変サイズの入力を固定サイズの出力(ハッシュ値)にマップします。 Javaオブジェクトの場合、出力は32ビット符号付き整数です。

JavaのHashtableは、実際のオブジェクトが格納される配列へのインデックスとしてハッシュ値を使用し、モジュロ演算と衝突を考慮します。ただし、これはハッシュではありません。

Java.util.HashMap実装は、場合によっては過度の衝突を防ぐために、インデックス付けの前にハッシュ値でいくつかの追加ビットスワッピングを実行します。 「追加のハッシュ」と呼ばれますが、これは正しい用語だとは思いません。

0
forty-two