предисловие
Сегодня проверил рыбу.HashMap
Исходный код, я думаю об интервью с Дашеном, и интервьюер спросилRedis 字典
а такжеHashMap
Чем отличается процесс хэширования. . . Честно говоря, я тоже видел дизайн и реализацию Redis (рекомендуется), но точно описать не могу, поэтому написал эту статью.
Примечание: Поскольку эта статья посвящена процессу хеширования, часть исходного кода зависит от простых для понимания описаний великих богов~
Хеш-функция (хеш-алгоритм)
-
хэш-функцияХеш-функция, также известная как хеш-алгоритм, представляет собой метод создания небольшого цифрового «отпечатка пальца» из любых данных, и этот «отпечаток пальца»хэш-значение.
-
Хеш-функции используются в самых разных областях, таких как защита данных, обеспечение передачи достоверной информации, хеш-таблицы и т. д. В этой статье в основном обсуждаются приложения на хеш-таблицах.
-
Конечно, мы надеемся, что хэш-функция может гарантировать, что каждому ключу соответствует «отпечаток пальца», то естьхэш-значение,Так называемыйидеальный хэш, но из-за соображений производительности, сценариев применения и т. д. можно принять не слишком многохэш-коллизия.
-
хэш-коллизия: вход и выход хэш-функции не являются уникальными соответствиями.Например, хеш-значения, полученные из входов A и B хеш-функции, оба являются C.
-
Общие хеш-функции:
прямая адресация цифровой анализ квадратный метод метод складывания остаточный метод метод случайных чисел
- Предположительно все знают сценарии HashMap и словаря Redis, и будут выбирать исходя изМетод остаткаОптимизирован для использования в качестве хэш-функции. Концепция каждого метода не будет здесь повторяться, пожалуйста, перейдите кПроверьте это здесь.
Решение Hash Collision (Hash Collision)
-
hash?->散列算法的选择->散列冲突怎么解决
Предположительно, это мнение большинства моих коллег. Итак, давайте посмотрим, каковы основные алгоритмы разрешения коллизий хэшей?
открытая адресация
- Как только произошла коллизия, переходим к поиску следующего пустого хеш-адреса, простейший алгоритм компании следующий.
Возьми каштан, поставь
Вместо 12 вставляем 26, 37 по очереди, (26+1)% 12 = (37+1)%12, есть конфликт хэшей, поэтому снова (37+2)%12 = 4, значение хеша другое, конфликт разрешен.
Перефразирование
- Несколько хэш-функций подготавливаются одновременно, и альтернативная хеш-функция может использоваться для вычисления, когда первая хеш-функция сталкивается.
метод цепного адреса
- Сначала используйте метод остатка, чтобы получить хеш-значение.Если хэш-значение конфликтует, вставьте конфликтующие узлы один за другим через связанный список, чтобы сформировать структуру, как показано ниже.
Следующий пример:
В сцене видно, что 48 % 12 = 12 % 12 = 0, образуя связанный список.
Закон об общей зоне разлива
- Используйте дополнительное общее хранилище для хранения элементов с конфликтующими хэш-значениями.
HashMap
Алгоритм хеширования HashMap
виньеткаLOAD_FACTOR
(коэффициент нагрузки)
-
каждый знает
HashMap
по умолчаниюLOAD_FACTOR
для0.75
, какова его функция? Следуйте исходному коду, чтобы следовать трассировке ниже ~ -
new
ОдинHashMap
, комментарии к исходному коду говорят намcapacity
16,load_factor
составляет 0,75.
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
- вместе с
put
Элементы, мы должны проводить расширение, увидетьresize()
Функция относится к знаковой части поста.
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
- Видно, что порог пройден через
capacity
умножить наload_factor
производное, то есть16 * 0.75 = 12
.HashMap
Если элементов больше, чем порог, емкость должна быть расширена, что можно понимать какНебольшой порог относительно емкости снижает вероятность коллизии хэшей, потому что вероятность коллизии хэшей с 16 элементами ниже, чем у 12 элементов при той же хэш-функции..
Формула и анализ хеш-функции
- Алгоритм хеширования HashMap аппроксимирует деление с остатком, но не работает
MOD
операций, но использовать побитовые операции, предполагаявходное значение, хэш-функция
Конкретная формула выглядит следующим образом:
f(key) = hash(key) & (table.length - 1)
hash(key) = (h = key.hashCode()) ^ (h >>> 16)
-
hash(key) & (table.length - 1)
правдаtable.length
Фактически аналогичный эффект имеет оптимизированная версия остатка, основанная на методе деления и оставления остатка. из-заHashMap
Возможности расширяются каждый разtable.length
будет, поэтому битовые операции значительно эффективнее.
-
>>>
это беззнаковый оператор сдвига вправо, мы знаемhashCode()
Диапазон значений очень широк, а сама возможность конфликта очень мала, но это согласуется с вышеизложенным.table.length - 1
Эта вероятность увеличивается, потому чтоtable.length
является небольшим значением. Вот почему используйте>>> 16
причина,hashCode()
Верны как старший, так и младший битыf(key)
При определенном влиянии распределение получается более равномерным, а вероятность коллизии хэшей меньше.
Разрешение конфликтов хэшей HashMap
-
HashMap
Разрешение хеш-коллизии, очевидно,метод цепного адреса, собственно, это и видно из конструкции. - Могу
HashMap
изresize()
Этот метод также можно увидеть, см. исходный код ниже. . . Примечания к .
if (oldTab != null) {
// 遍历旧数组上的节点
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
// 当前链表只有一个节点(没有散列冲突的情况)
if (e.next == null)
// 通过散列算法计算存放位置并放入
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
// 红黑树去了。。。
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
// 低位链表、高位链表
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
// 遍历发生散列冲突的链表
next = e.next;
// hash值小于旧数组容量 放入低位链表
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
// hash值大于等于旧数组大小 放入高位链表
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
// 低位链表放在原来index下
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
// 高位链表放在原来index + 旧数组大小
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
Словарь Redis
Введение в структуры данных
а такжеHashMap
примерно в том же месте
Во-первых, краткое описание структуры данных словаря Redis было полностью затенено, когда я учился в колледже.C
язык.
typedef struct dictht {
// 哈希表数组
dictEntry **table;
// 哈希表大小
unsigned long size;
// 哈希表大小掩码,用于计算索引值
// 总是等于size - 1
unsigned long sizemask;
// 哈希表已使用节点数
unsigned long used;
} dictht
- Есть массив хэш-таблиц, он выглядит знакомо, и
Node<K,V>[]
Аналогично по эффекту, давайте посмотримdictEntry
этот класс.
typedef struct dictEntry {
// 键
void *key
// 值
union {
void *val;
unit64_tu64;
int64_ts64;
} v
// next指针
struct dictEntry *next;
}
-
Снова немного знакомая пара ключ-значение!
key
свойства содержат ключи в парах ключ-значение, аv
Свойства содержат значения, где значение пары ключ-значение может быть указателем,unit64_t
,int64_t
Целое.next
Указатель на другой узел хеш-таблицы, который может соединять несколько хэш-таблиц с одной и той же парой ключ-значение в связанный список. -
Видно, что до сих пор
HashMap
По сути, разницы нет, за исключением нескольких дополнительных свойств (размер хеш-таблицы, количество используемых узлов, маска и т. д.).
разница
- Я только что увидел, что нижний слой словаря реализован структурой хеш-таблицы, так что же представляет собой истинное лицо словаря?
typedef struct dict {
// 类型特定函数
dictType *type;
// 私有数据
void *privdata;
// 哈希表(上文讲的)
dictht ht[2];
// rehash索引
// 当rehash不在进行时,值为-1
int trehashidx;
}
- До сих пор была представлена структура данных словаря Redis.На следующем рисунке показан словарь в нормальном состоянии.
Алгоритм хеширования словаря Redis
- рассчитатьхэш-значениеПроцесс вычисления хэшей для функций на основе словаря, которые вычисляют хэши:
hash = dict -> type->hashFunction(key)
- Используйте атрибут SizeMask и значение HASH HASH HASH TALL для расчета значения индекса, в зависимости от ситуации
ht[0]
илиht[1]
. На самом деле иHashMap
изhash(key) & (table.length - 1)
то же самое, потому что в примечаниях говоритсяsizemask
всегда равноsize - 1
.
index = hash & dict->ht[x].sizemask
- Алгоритм вычисления хеш-значения, используемый Redis, таков:
MurmurHash2
, автор не может объяснить,связь дается.
Разрешение конфликтов хэшей словаря Redis
- Хэш-таблица Redis также используетметод цепного адреса, каждый узел имеет
next
Указатель, несколько узлов образуют односвязный список, иHashMap
Разница в том, что, поскольку хвостового указателя нет, используйтепробка для головыДобавьте новый узел в начало связанного списка.
Разница между Redis и HashMap
Глядя на хэш-алгоритм и разрешение хеш-конфликтов, особой разницы нет, так в чем же разница? То естьПерефразирование.
Rehash
-
Как упоминалось ранее, в структуре данных словаря есть две хеш-таблицы (
ht[2]
), секрет здесь. -
Rehash
Цель состоит в том, чтобы поддерживать коэффициент загрузки хеш-таблицы в разумных пределах.расширить или сжать. -
Действуйте следующим образом:
- для словаря
ht[1]
Хеш-таблицы выделяют пространство, если вы выполняетерасширятьоперация, тоht[1]
Размер в первую очередь больше или равенht[0].used *2
из; если исполнениесокращатьоперация, то
ht[1]
Размер первого больше или равенht[0].used
из.
- будет сохранен в
ht[0]
Все пары ключ-значение вrehash
прибытьht[1]
выше, т. е. пересчитать значения хэша и индекса ключа, а затем поместить пару ключ-значение вht[1]
в указанном месте в хеш-таблице. - когда
ht[0]
Все содержащиеся пары ключ-значение переносятся вht[1]
Позже(ht[0]
становится пустой хэш-таблицей), отпуститеht[0]
,Будуht[1]
Установить какht[0]
, И вht[1]
Заново создайте пустую хеш-таблицу в следующий разrehash
подготовиться к.
- для словаря
-
Примечание: Progressive Rehash в этой статье не упоминается.Автор рисунка действительно немотивирован.Структура данных более сложная,пожалуйста поймите.
Суммировать
В этой статье кратко анализируется алгоритм хеширования и решение хеш-коллизии.HashMap
,Redis 字典
изhash
, взгляды не обязательно правильные, прошу критиковать и исправлять!
использованная литература
- en.wikipedia.org/wiki/Хеш-функции
- Дизайн и реализация Redis
- Ууху. Call.com/question/26…