Чтобы расширить ответ Дэвида Ричерби, термин « хэш-функция » немного перегружен. Часто, когда мы говорим о хэш-функции, мы думаем о MD5, SHA-1 или о чем-то вроде .hashCode()
метода Java , который превращает некоторый ввод в одно число. Однако домен этого числа (т. Е. Является максимальным значением) вряд ли будет иметь тот же размер, что и хеш-таблица, в которой вы пытаетесь сохранить данные. (MD5 составляет 16 байт, SHA-1 составляет 20 байт и .hashCode()
представляет собой int
- 4 байт).
Итак, ваш вопрос касается следующего шага - когда у нас есть хеш-функция, которая может отображать произвольные входные данные в числа, как мы можем поместить их в структуру данных определенного размера? С другой функцией, также называемой «хэш-функцией»!
Тривиальный пример такой функции - по модулю ; Вы можете легко сопоставить число произвольного размера с определенным индексом в массиве по модулю. Это вводится в CLRS как «метод деления»:
В методе деления для создания хеш-функций мы отображаем ключ в один из слотов, беря остаток от деленный на . То есть хеш-функциям к мКмКм
мh ( k ) = k mod .м
...
При использовании метода деления мы обычно избегаем определенных значений . Например, не должно быть степенью 2, так как если то - это просто младших битов .m m = 2 p h ( k ) p kммм = 2пч ( к )пК
~ Введение в алгоритмы, §11.3.1 - CLRS
Таким образом, модуль не является отличной хеш-функцией, поскольку ограничивает размеры, которые мы можем безопасно использовать для нашей базовой структуры данных. В следующем разделе представлен чуть более сложный «метод умножения», который также использует модуль по модулю, но имеет преимущество, поскольку «значение не является критическим». Однако он лучше всего работает с некоторыми предварительными знаниями о «характеристиках хешируемых данных» - то, чего мы часто не знаем.m
Java HashMap
использует модифицированную версию метода деления, которая выполняет этап предварительной обработки для учета слабых .hashCode()
реализаций, чтобы он мог использовать массивы степени двойки. Вы можете точно увидеть, что происходит в .getEntry()
методе (мои комментарии):
// hash() transforms key.hashCode() to protect against bad hash functions
int hash = (key == null) ? 0 : hash(key.hashCode());
// indexOf() converts the resulting hash to a value between 0 and table.length-1
for (Entry<K,V> e = table[indexFor(hash, table.length)];
...
Java 8 принесла переписывание, HashMap
которое еще быстрее, но немного сложнее для чтения. Однако он использует тот же общий принцип для поиска по индексу.