При нормальной разработке HashMapяНаиболее часто используемый класс Map (нет ни одного), он поддерживает нулевые ключи и нулевые значения и является первым выбором для большинства сценариев доступа с использованием пар ключ-значение. Следует помнить одну вещь: HashMap не является потокобезопасной структурой данных, поэтому не используйте ее в многопоточных сценариях.
Обычно основной целью использования Map является поставить (положить), получить доступ (получить) или удалить (удалить), и особых требований к порядку нет — HashMap в данном случае лучший выбор.
01. Хэш
Для HashMap трудно понять не Map, а Hash.
Хэш, обычно переводится как «хэш», также напрямую транслитерируется как «хеш», что это значит? Это отображение данных любой длины в поле фиксированной длины (хеш-значение) с помощью алгоритма.
Чтобы быть более интуитивным, нужно смешать строку данных wang и вывести другую er данных фиксированной длины в качестве функции data wang. Обычно мы используем цепочку отпечатков пальцев для отображения определенного человека. Не стоит недооценивать отпечаток пальца размером с ваш палец. алгоритм тоже очень мощный, у вас есть?).
Для любых двух разных блоков данных вероятность одинакового значения хеш-функции крайне мала, то есть для заданного блока данных крайне сложно найти блок данных с одинаковым значением хеш-функции. Кроме того, для блока данных, даже если изменится только один его бит, изменение его хэш-значения будет очень большим — это значение хэша!
В Java хеш-значение строки String вычисляется следующим образом:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
Неважно, понимаете вы или нет, мы относимся к этому как к алгоритму «умножение-сложение итеративной операции». Пользуясь случаем, давайте взглянем на хэш-значения четырех строк «Shen», «Mo», «Wang» и «two».
String[] cmower = { "沉", "默", "王", "二" };
for (String s : cmower) {
System.out.println(s.hashCode());
}
Вывод выглядит следующим образом (5 цифр):
Хэш-значение Шена: 27785 Хэш-значение по умолчанию: 40664. Королевский хэш: 29579 Хэш из двух: 20108
Для HashMap назначение Hash (ключ, позиция ключа) - ускорить поиск пар ключ-значение (думаете, если телефонная книга не упорядочена по первой букве имени человека, как это было бы сложно чтобы найти человека «Мои друзья в WeChat». Многие из них добавили букву А перед своим ником, так жестоко»). Обычно мы привыкли использовать String в качестве ключа карты, см. следующий код:
Map<String, String> map = new HashMap<>();
String[] cmower = { "沉", "默", "王", "二" };
for (String s : cmower) {
map.put(s, s + "月入25万");
}
Будет ли HashMap действительно использовать String в качестве фактического ключа? Давайте посмотрим на исходный код метода put HashMap:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
Хотя только одинputVal()
вызов метода, но вы должны были обнаружить, что ключ будет выполнять хеш-операцию внутри HashMap, конкретный код выглядит следующим образом:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
Если ключ представляет собой строку String,hash()
Сначала получается hashCode (хэш-значение) строки, затем находится хэш-значение, а окончательное значение является фактическим ключом (значение int) HashMap.
Так как HashMap использует хеш-значение ключа в качестве фактического ключа при вводе, то при получении значения по ключу естественно сначалаget(key)
Ключ метода хэшируется, см. следующий код:
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
02. Как разрешать конфликты хеш-значений
Хотя хеш-значение трудно повторить, мы должны понимать, что это преобразование представляет собой карту сжатия, то есть пространство хэш-значения обычно намного меньше пространства ввода, и разные входы могут хэшироваться в один и тот же вывод.
То есть ключ1 ≠ ключ2, но функция(ключ1) может быть равна функции(ключ2) — значения хеша сталкиваются. что делать?
Самое простое решение, чтобы подумать: когда значение Hahh Value Key2 Key2 конфликтает с значением HASH Value1, другое значение HASH значение1 генерируется на основе значения. Если значение1 и значение больше не конфликтует, то используйте значение1 в качестве хеша Значение Key2.
Таким образом, всегда будет найден тот, который не конфликтует.
03. Начальная мощность и коэффициент загрузки
Существует три основных метода построения HashMap:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
где initialCapacity — начальная мощность (1
Когда количество записей в HashMap превышает произведение коэффициента загрузки и текущей емкости, HashMap необходимо расширить, чтобы примерно удвоить количество сегментов.
Как правило, коэффициент загрузки по умолчанию (0,75) представляет собой компромисс между затратами времени и места. Высокий коэффициент загрузки снижает объем пространства, но в то же время увеличивает стоимость запросов. Если коэффициент загрузки слишком мал, следует увеличить начальную мощность, иначе будет происходить частое увеличение мощности.
Необходимое количество записей в карте и коэффициент ее загрузки следует учитывать при настройке начальной емкости, чтобы свести к минимуму количество операций расширения.
Если вы можете заранее предсказать количество пар ключ-значение, к которым нужно получить доступ, вы можете рассмотреть возможность установки соответствующей начальной емкости (больше, чем «оценочное количество элементов/коэффициент загрузки», и является степенью 2).
04. Резюме
Долгое время мои знания о HashMap ограничивались его использованием.put(key, value)
иvalue = get(key)
.
Но, когда я заставил себя каждую неделю выводить техническую статью по Java, я действительно "понял" HashMap - значение хэша (hash value), коллизия хешей (коллизия хэшей), начальная емкость и коэффициент загрузки, на самом деле может стоять перед меня и смеяться все время - и изначально, я убежал, когда я увидел эти ключевые слова, я боялся их видеть.