Подтвердить что ты не робот

Объяснение метода hashMap # hash (int)

Может кто-нибудь объяснить мне статический метод хэш-хэш (int) HashMap #?

Какое оправдание для создания равномерно распределенных хэшей?

/**
 * 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);
}

Пример облегчит переваривание.

Разъяснение Я знаю об операторах, таблицах истинности и побитовых операциях. Я просто не могу реально декодировать реализацию или комментарий. Или даже рассуждения позади.

4b9b3361

Ответ 1

>>> - это логический сдвиг вправо (без расширения знака) (JLS 15.19 Shift Operators), а ^ - побитовое exclusive-or (JLS 15.22.1 Побитовые операторы с целыми числами).

Что касается этого, то в документации есть подсказка: HashMap использует таблицы с длиной в две строки и хеширует ключи, маскируя высшие биты и беря только младшие биты их хэш-кода.

// HashMap.java -- edited for conciseness
static int indexFor(int h, int length) {
    return h & (length-1);
}

public V put(K key, V value) {
    int hash = hash(key.hashCode());
    int index = indexFor(hash, table.length);
    // ...
}

Итак, hash() пытается привести релевантность к более высоким битам, которые в противном случае будут скрыты (indexFor в основном отбрасывает более высокие бит h и принимает только нижние бит k, где length == (1 << k)).

Контрастируйте это с помощью способа Hashtable (который не должен иметь таблицу длины в две строки) использует хэш-код ключа.

// Hashtable.java -- edited for conciseness
public synchronized V get(Object key) {
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % table.length;
    // ...
}

Выполняя более дорогостоящую операцию % (вместо простой маскировки бит), производительность Hashtable менее чувствительна к хеш-кодам с плохим распределением в младших битах (особенно если table.length является простым числом).

Ответ 2

Я не знаю, как все смежные работы, но мотивация изложена в комментариях:

Способ реализации HashMap основан на том, что функция hashCode достаточно хорошо реализована. В частности, младшие бит хэш-значения должны распределяться равномерно. Если у вас много коллизий на младших битах, HashMap не будет работать хорошо.

Поскольку реализация hashCode находится вне контроля HashMap (каждый объект может реализовать свои собственные), они предоставляют дополнительную хэш-функцию, которая немного меняет объект hashCode, чтобы гарантировать, что младшие биты распределяются более случайным образом. Опять же, я не знаю, как это работает в точности (или насколько оно эффективно), но я полагаю, что это зависит, по крайней мере, от того, что более высокие биты распределяются одинаково (он, кажется, мешает более высоким битам в младшие биты).

Так что это делается, чтобы попытаться минимизировать конфликты (и, следовательно, повысить производительность) при наличии плохо реализованных методов hashCode.