Щоб розширити відповідь Девіда Річербі, термін " хеш-функція " трохи перевантажений. Часто, коли ми говоримо про хеш-функцію, ми думаємо про MD5, SHA-1 або щось на зразок .hashCode()
методу Java , який перетворює деякий вхід в єдине число. Однак домен цього числа (тобто це максимальне значення) дуже малоймовірно , щоб бути тим же розміром , як Hashtable ви намагаєтеся зберігати дані в MD5 (16 байт, SHA-1 становить 20 байт, а. .hashCode()
Це int
- 4 байт).
Отже, ваше питання стосується наступного кроку - як тільки у нас є хеш-функція, яка може зіставляти довільні введення до чисел, як ми можемо їх розмістити в структурі даних певного розміру? З іншою функцією, яку також називають "хеш-функцією"!
Тривіальним прикладом такої функції є модуль ; Ви можете легко зіставити кількість довільних розмірів на конкретний індекс у масиві з модулем. Це вводиться в CLRS як "метод поділу":
У способі поділу для створення хеш-функцій ми відображаємо ключ в один з слотів, беручи решту поділену на . Тобто хеш-функція єm k mкмкм
mh ( k ) = k mod .м
...
Використовуючи метод поділу, ми зазвичай уникаємо певних значень . Наприклад, не повинна бути потужністю 2, оскільки якщо то - це просто бітів нижнього порядку .m m = 2 p h ( k ) p kммm = 2pгод ( к )pк
~ Вступ до алгоритмів, §11.3.1 - CLRS
Таким чином, модуль не є чудовою хеш-функцією, оскільки він обмежує, які розміри ми можемо безпечно використовувати для основної структури даних. Наступний розділ представляє трохи складніший "метод множення", який також використовує модуль, але є вигідним, оскільки "значення не є критичним". Однак він найкраще працює з деякими попередніми знаннями "характеристик хешованих даних" - те, що ми часто не знаємо.м
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
яке ще швидше, але трохи важче для читання. Однак він використовує той же загальний принцип для пошуку індексу.