Обзор HashMap
Hashmap - это асинхронная реализация интерфейса карты на основе хэш -таблицы. Эта реализация предоставляет все дополнительные операции отображения и позволяет использовать нулевые значения и нулевые клавиши. Этот класс не гарантирует порядок отображений, особенно он не гарантирует, что заказ продлится.
Структура данных HashMap
На языке программирования Java есть две основные структуры, одна - массив, а другая - моделируемый указатель (ссылка). Все структуры данных могут быть построены с использованием этих двух основных структур, а HashMap не является исключением. HashMap на самом деле является структурой данных «связанный список хэш», то есть структуру массивов и связанных списков, но в JDK1.8
Внедрение красного и черного дерева добавлена. Когда длина связанного списка превышает 8, он преобразуется в структуру красного и черного дерева.
Как видно из приведенного выше рисунка, HashMap использует метод адреса цепного адреса в Java. Проще говоря, метод адреса ссылки представляет собой комбинацию массивов и связанных списков. На каждом элементе массива есть связанная структура списка. Когда данные хешируются, получен апобпиз массива, и данные помещаются в связанный список соответствующих элементов подписания.
*/ Статический узел класса <K, V> реализует Map.Entry <K, v> {final int hash; // Используется для поиска позиции индекса массива конечного K клавиша K; V Значение; Node <K, V> Next; // Next Node в связанном списке Node (int hash, k -ключ, v Значение, узел <k, v> next) {this.hash = hash; this.key = key; this.value = значение; this.next = Далее; }Узел-это внутренний класс Hashmap, который реализует интерфейс Map.Entry, который по сути является картой (пара ключей).
Иногда два клавиша расположены в одном и том же положении, что указывает на столкновение хэш. Конечно, чем более равномерны результаты расчета хэш -алгоритма, тем меньше вероятность столкновения хэша и тем выше эффективность доступа карты.
В классе HashMap есть очень важное поле, которое является таблицей узла [], то есть хэш -ведром. Это, очевидно, массив узлов.
Если массив хеш -ведра большая, даже бедный алгоритм хэш будет более разбросанным. Если массив матрицы хеш -ведра невелик, даже хороший хэш -алгоритм будет иметь больше столкновений, поэтому необходимо взвесить пространственную стоимость и временную стоимость. Фактически, он должен определить размер массива хеш -ведра, основанный на фактической ситуации, и на этой основе, разработанный алгоритм хэш -хэша снизит хэш -столкновения. Итак, как мы можем контролировать карты, чтобы сделать вероятность хеш -столкновений небольшими, а хеш -массивы (узел [] таблицы) занимают меньше места? Ответ - хороший алгоритм хэш -алгоритм и расширение пропускной способности.
Прежде чем понять хеш -процесс и процесс расширения, мы должны понять несколько областей HashMap. Из исходного кода конструктора HashMap по умолчанию можно видеть, что конструктор инициализирует следующие поля, исходный код заключается в следующем:
int порог; // значение ключа, которое может быть приспособлено, является Ultimate Float LoadFactor; // коэффициент загрузки int modcount; int size;
Во-первых, длина инициализации таблицы узла [] (значение по умолчанию составляет 16), коэффициент нагрузки является коэффициентом нагрузки (значение по умолчанию составляет 0,75), а порог-это количество узла (пары клавишных значений) максимальной суммы данных, которое может приспособить хэшмап. Порог = длина * Коэффициент нагрузки. То есть после того, как массив определил свою длину, чем больше коэффициент нагрузки, тем больше пар клавишных значений он может вместить.
Основываясь на формуле определения коэффициента нагрузки, можно видеть, что порог представляет собой максимальное количество элементов, разрешенных в соответствии с этим коэффициентом нагрузки и длиной (длина массива). Если это число превышает это, измените размер (увеличить емкость). Расширенная емкость HashMap в два раза превышает предыдущую емкость. Коэффициент загрузки по умолчанию 0,75 является сбалансированным выбором для эффективности пространства и времени. Рекомендуется не изменять его, если только в случае особого времени и пространства, если есть много пространства для памяти и требований к высокой эффективности времени, значение коэффициента нагрузки нагрузки может быть уменьшено; Напротив, если пространство памяти ограничено и требования к эффективности времени не высоки, значение коэффициента нагрузки может быть увеличено, что может быть больше 1.
Поле размера на самом деле легко понять, это количество пар клавишных значений, которые на самом деле существуют в Hashmap. Обратите внимание на разницу между длиной таблицы и количеством порогов, которые соответствуют парам максимального значения ключа. Поле Modcount в основном используется для записи количества изменений во внутренней структуре HashMap и в основном используется для быстрой сбоя итерации. Чтобы подчеркнуть, изменения во внутренней структуре относятся к изменениям в структуре, такие как поставки новых парам ключевых значений, но значение, соответствующее ключу, перезаписано и не принадлежит к структурным изменениям.
В Hashmap длина хэш -ведро матрицы должна быть в мощности N 2 (должно быть составное число). Это нетрадиционный дизайн. Обычный дизайн состоит в том, чтобы разработать размер ведра в качестве основного числа. Относительно говоря, вероятность конфликта, вызванная основными числами, меньше, чем у составных чисел. Для конкретных доказательств, пожалуйста, см. //Www.vevb.com/article/100911.htm. Hashtable инициализирует размер ведра до 11, который представляет собой применение размера ковша, разработанного в качестве основных чисел (хэштата не может быть гарантированно будет основным числам после расширения). HashMap принимает этот нетрадиционный дизайн, в основном для оптимизации при модуле и расширении. В то же время, чтобы уменьшить конфликты, HashMap также добавляет процесс высокого участия в вычислениях при определении расположения позиции индекса хэш-ведра.
Здесь есть проблема. Даже если коэффициент нагрузки и алгоритм хэш разработаны разумно, неизбежно будет ситуация, когда молния слишком длинная. Как только молния станет слишком длинной, это серьезно повлияет на производительность HashMap. Следовательно, в версии JDK1.8 структура данных была еще более оптимизирована, а красное и черное дерево было введено. Когда длина связанного списка слишком длинная (по умолчанию больше 8), связанный список преобразуется в красное и черное дерево. Характеристики быстрого добавления, удаления, модификаций и поисков красного и черного дерева будут использоваться для повышения производительности HashMap. Вставка, удаление и поиск красных и черных деревьев будут использоваться для вставки, удаления и поиска алгоритмов, таких как красное и черное дерево.
Определите положение индекса массива хеш -ведра
Реализация кода:
// Метод 1: Статический окончательный int hash (ключ объекта) {//jdk1.8 & jdk1.7 int h; // h = key.hashcode () получает значение хэшкода для первого шага // h ^ (h >>> 16) участвовать в работе второго шага возврата (Key == null)? 0: (h = key.hashcode ()) ^ (h >>> 16);} // Метод 2: Статический int indexfor (int h, int length) {//jdk1.7 Исходный код, jdk1.8 не имеет этого метода, но принцип реализации является тем же возвратом H & (Length-1); // Третий шаг выполняет работу модуля}Хэш-алгоритм здесь представляет собой, по сути, три шага: принятие значения хэшкода ключа, операции с высоким содержанием и модуля.
Для любого данного объекта, до тех пор, пока его возвращаемое значение HashCode () одинаково, хэш -код, рассчитываемый методом вызова программы, всегда одинаков. Первое, о чем мы думаем, это модуль хеш -значения до длины массива, чтобы распределение элементов было относительно однородным. Тем не менее, потребление операций модуля является относительно большим. Это сделано в HashMap: метод вызови два, чтобы вычислять, какой индекс объект должен храниться в массиве таблиц.
Этот метод очень умный. Он получает сохраненные биты объекта через H & (Table.Length -1), а длина базового массива HashMap всегда до N -силовой мощности 2, что является оптимизацией HashMap с точки зрения скорости. Когда длина всегда подходит к мощности N 2, операция H & (длину-1) эквивалентна длине модуля, то есть длиной H %, но и имеет более высокую эффективность, чем %.
В реализации JDK1.8 алгоритм для операций с высоким содержанием бита оптимизирован, а высокая 16-битная XOR Low-16-битная реализация HashCode (): (H = K.HashCode ()) ^ (H >>> 16), что в основном рассматривается из скорости, эффективности и качества. Это может гарантировать, что когда длина стола массива относительно невелика, это также может гарантировать, что биты участвуют в хэш-расчетах с учетом высокого уровня, и не будет чрезмерных накладных расходов.
Вот пример, n - длина таблицы.
HashMap PUT MEDENTION Реализация
Общая идея функции пута составляет:
Конкретный код реализуется следующим образом:
public v put (k key, v value) {return putval (hash (key), key, значение, false, true); } / ***Метод генерации хэш* / статический конечный int hash (объект клавиша) {int h; возврат (Key == null)? 0: (h = key.hashcode ()) ^ (h >>> 16); } final v putval (int hash, k -ключ, v Значение, логическое только что -тофабсент, логический выкат) {node <k, v> [] tab; Узел <K, V> P; int n, я; // Судите, является ли таблица пустой, if ((tab = table) == null || (n = tab.length) == 0) n = (tab = reszize ()). Length; // Создание новой массивы таблиц и получить длину массива // Рассчитайте значение хэша для вставленного индекса массива I в соответствии с ключом значения ключа. Если таблица [i] == null, напрямую создайте новый узел и добавьте if ((p = tab [i = (n - 1) & hash]) == null) вкладка [i] = newnode (hash, key, value, null); else {// Если соответствующий узел имеет узел <k, v> e; K K; // судить, является ли первый элемент таблицы [i] такой же, как ключ, если то же самое, непосредственно перезаписать значение if (p.hash == hash && ((k = p.key) == key || (key! = Null && key.equals (k))))) e = p; // Судите, является ли таблица [i] трентодом, то есть, является ли таблица [i] красным черным деревом. Если это красно-черное дерево, непосредственно вставьте пару клавишных значений в дерево, иначе if (p exanceof treeNode) e = ((treeNode <K, v>) p). PUTTREEVAL (это, вкладка, хэш, ключ, значение); // Эта цепочка представляет собой связанный список else {// транспорт через таблицу [i], чтобы определить, превышает ли длина связанного списка, чем treeify_threshold (значение по умолчанию составляет 8). Если он больше 8, преобразуйте связанный список в красное черное дерево и выполните операцию вставки в красном черном дереве. В противном случае выполняется операция вставки связанного списка; Если обнаружено, что ключ уже имеет прямое значение перезаписи во время процесса обхода; for (int bincount = 0;; ++ bincount) {if ((e = p.next) == null) {p.next = newnode (хэш, ключ, значение, null); if (bincount> = treeify_threshold - 1) // -1 для 1 -й Treeifybin (Tab, hash); перерыв; } if (e.hash == hash && ((k = e.key) == key || (key! = null && key.equals (k)))) break; P = E; }} // Записать if (e! = Null) {// Существующее отображение для ключа v oldValue = e.value; if (! только ifabsent || oldvalue == null) e.value = value; Днемодеацирование (e); вернуть OldValue; }} ++ modcount; // После успешной вставки определите, превышает ли фактическое количество паров значений ключей к пороговому значению максимальной емкости. Если он превышает емкость, разверните if (++ size> порог) resize (); Днемадезинг (выселение); вернуть ноль; }Hashmap Get реализация метода
Идея заключается в следующем:
1. Первый узел в ведре, ударил напрямую;
2. Если есть конфликт, используйте Key.equals (k), чтобы найти соответствующую запись
Если это дерево, то ищите в дереве через key.equals (k), o (logn);
Если это связанный список, то поищите через key.equals (k) в связанном списке, O (n).
public v get (object key) {node <k, v> e; return (e = getNode (hash (key), key)) == null? NULL: E.Value; } окончательный узел <K, v> getNode (int hash, object key) {node <k, v> [] tab; Узел <K, V> Сначала, E; int n; K K; if ((tab = table)! = null && (n = tab.length)> 0 && (first = tab [(n - 1) & hash])! = null) {// Хит непосредственно if (first.hash == hash && // Каждый раз, когда он проверяется первый узел ((k = первый. // Пропущены if ((e = first.next)! = Null) {// get if (первый экземпляр TREENODE) return ((TreeNode <K, V>) сначала) .getTreeNode (hash, key); // get do {if (e.hash == hash && ((k = e.key) == key || (key! = Null && key.equals (k)))) return e; } while ((e = e.next)! = null); }} return null; }Механизм расширения пропускной способности
Изменение размера означает, что пересчитывание способности и постоянно добавляя элементы в объект HashMap. Когда массив внутри объекта HashMap не может загружать больше элементов, объект должен расширить длину массива, чтобы можно было загрузить больше элементов. Конечно, массивы в Java не могут быть автоматически расширены. Метод состоит в том, чтобы использовать новый массив вместо существующих массивов с небольшой пропускной способностью, точно так же, как мы используем небольшое ведро для заполнения воды. Если мы хотим заполнить больше воды, мы должны заменить ее большим ведром.
Мы анализируем исходный код RESIZE. Учитывая, что JDK1.8 интегрирует красные и черные деревья, это сложнее. Чтобы облегчить понимание, мы все еще используем код JDK1.7, который легче понять. Есть небольшая разница в сущности. Давайте поговорим о конкретных различиях позже.
void reszize (int newcapacity) {// пауза новая запись емкости [] oldtable = table; // Ссылка на массив входа перед расширением int oldCapacity = oldtable.length; if (OldCapacity == Maximum_capacity) {// Если размер массива перед расширением достиг максимального (2^30) Threshold = integer.max_Value; // Измените порог до максимального значения int (2^31-1), чтобы емкость не была расширена в будущем доходности; } Inpit [] newtable = new intry [newCapacity]; // инициализировать новую передачу массива входа (Newtable); //! ! Передача данных в новую таблицу массива входа = Newtable; // атрибут таблицы HashMap относится к новой Throshold Marray = (int) (NewCapacity * LoadFactor); // изменять порог}Здесь мы используем массив с большей емкостью вместо существующего массива с меньшей емкостью. Метод Transfer () копирует элементы оригинального массива входа в новый массив записей.
void Transfer (entry [] newtable) {inpit [] src = table; // src относится к старому массиву входа int newcapacity = newtable.length; for (int j = 0; j <src.length; j ++) {// спокойствие через старую запись массива входа <K, v> e = src [j]; // Получить каждый элемент старого массива входа if (e! = Null) {src [j] = null; // Отпустите ссылку на объект старого массива входа (после цикла для старого входа больше не относится ни к любому объекту) do {intry <k, v> next = e.next; int i = indexfor (e.hash, newcapacity); //! ! Пересчитывать положение каждого элемента в массиве e.next = newtable [i]; // Tag [1] newtable [i] = e; // Поместите элемент на массив E = Далее; // Доступ к элементу в следующей цепочке записи} while (e! = Null); }}}Ссылка на новичок [i] присваивается E.Next, что означает, что используется метод вставки заголовка одного связанного списка. Новые элементы в том же положении всегда будут размещены в главе связанного списка; Таким образом, элементы, размещенные на индексе, в конечном итоге будут размещены в конце цепочки входа (если возникает хэш -конфликт). Это отличается от JDK1.8, что подробно объясняется ниже. Элементы на одной и той же цепочке входа в старом массиве могут быть размещены в разных положениях в новом массиве после пересчитывания позиции индекса.
Вот пример, чтобы проиллюстрировать процесс расширения емкости. Предположим, что наш хэш -алгоритм просто использует ключевой мод, чтобы получить размер таблицы (то есть длина массива). Размер таблицы массивов хеш -ведра = 2, поэтому ключ = 3, 7, 5, а порядок пута составляет 5, 7 и 3. После мода 2 конфликт находится в таблице [1]. Здесь предполагается, что коэффициент нагрузки нагрузки factor = 1, то есть, когда фактический размер пары ключевых значений больше, чем фактический размер таблицы, он расширяется. Следующие три шага - это процесс изменения размера массива хэш -ведра до 4, а затем перефразировать все узлы.
Давайте объясним, какие оптимизации были сделаны в JDK1.8. После наблюдения мы можем обнаружить, что мы используем силу двух расширения (ссылаясь на длину в два раза больше исходного), поэтому положение элемента находится либо в исходном положении, либо перемещает положение мощности двух снова в исходном положении. Глядя на рисунок ниже, вы можете понять значение этого предложения. n - длина таблицы. Рисунок (а) представляет пример положения индекса двух ключей, которые определяют положение индекса ключа1 и ключа до расширения. Рисунок (b) представляет пример положения индекса ключа1 и ключа2 после расширения. Где HASH1 является хэш-и высоким результатом работы, соответствующим Key1.
После того, как элемент пересчитывает хэш, поскольку n становится 2 раза, диапазон маски N-1 составляет 1 бит (красный) в высокой точке, поэтому новый индекс изменится так:
Поэтому, когда мы расширяем HashMap, нам не нужно пересчитывать хэш, как реализация JDK1.7. Нам просто нужно посмотреть, будет ли бит, добавленный к исходному значению хэша, 1 или 0. Если это 0, индекс не изменился. Если это 1, индекс становится «оригинальным индексом + OldCap». Вы можете увидеть следующую цифру как диаграмму изменения размера с 16 расширением до 32:
Эта конструкция действительно очень умна, что не только экономит время для пересчета хеш -значения, но и, поскольку вновь добавленный 1 -бит составляет 0 или 1, его можно считать случайным, поэтому процесс изменения размера равномерно распределяет предыдущие противоречивые узлы к новому ведро. Это новая точка оптимизации, добавленная JDK1.8. Есть немного внимания к разнице. Когда перефразируется в JDK1.7, когда старые связанные списки мигрируют новые связанные списки, если позиция индекса массива новой таблицы одинакова, связанные элементы списка будут инвертированы, но, как видно из приведенного выше рисунка, JDK1.8 не будет перевернут. Заинтересованные студенты могут изучать исходный код RESIZE JDK1.8, что очень хорошо, следующим образом:
Окончательный узел <k, v> [] resize () {node <k, v> [] oldtab = table; int oldcap = (oldtab == null)? 0: oldtab.length; int oldthr = порог; int newcap, newthr = 0; if (oldcap> 0) {// Если максимальное значение превышает, оно больше не будет расширено, поэтому я должен столкнуться с вами, если (OldCap> = maximum_capacity) {threshold = integer.max_value; вернуть Oldtab; } // Если максимальное значение не превышено, оно будет расширено до 2 раза больше исходного if ((newcap = oldCap << 1) <maximum_capacity && oldCap> = default_initial_capacity) newthr = oldthr << 1; // двойной порог} else if (oldTHR> 0) // Начальная емкость была размещена в пороге newCap = oldTHR; else {// нулевой начальный порог обозначает использование дефолтов newcap = default_initial_capacity; newthr = (int) (default_load_factor * default_initial_capacity); } // Рассчитайте новый верхний предел изменения размера if (newthr == 0) {float ft = (float) newcap * loadfactor; newthr = (newcap <maximum_capacity && ft <(float) maximum_capacity? (int) ft: integer.max_value); } threshold = newthr; @Suppresswarnings ({"rawtypes", "unchecked"}) узел <K, v> [] newtab = (node <k, v> []) new Node [newcap]; таблица = newtab; if (oldtab! = null) {// переместить каждое ведро в новые ведра для (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 exanceof treeNode) ((TreeNode <K, v>) e) .split (this, newtab, j, oldcap); else {// сохранить узел порядка <K, v> lohead = null, lotail = null; Узел <k, v> hihead = null, Hitail = null; Узел <K, V> Далее; do {next = e.next; // оригинальный индекс if ((e.hash & oldcap) == 0) {if (lotail == null) loehead = e; else lotail.next = e; lotail = e; } // оригинальный индекс + oldcap else {if (ittail == null) hihead = e; else Hitail.next = E; Hitail = E; }} while ((e = Next)! = null); // Поместите исходный индекс в ведро, если (lotail! = Null) {lotail.next = null; newtab [j] = lohead; } // Поместите исходный индекс + oldcap в ведро if (Hitail! = Null) {witail.next = null; newtab [j + oldcap] = hihead; }}}}} return newtab;}Суммировать
Теперь мы можем ответить на несколько вопросов в начале, чтобы углубить наше понимание HashMap:
1. Когда будет использован хэшмап? Каковы его характеристики?
Он основан на реализации интерфейса карты. При хранении пар клавишных значений он может получать нулевые значения ключей, которые являются асинхронными. HashMap хранят вход (хэш, ключ, значение, следующее) объекты.
2. Вы знаете, как работает hashmap?
С помощью хэш -метода объекты хранятся и получают через Put and Get. При хранении объекта, когда мы передаем K/V методу PUT, он вызывает хэшкод для расчета хэша, чтобы получить местоположение ковша и сохранить его дальше. HashMap автоматически отрегулирует емкость в соответствии с текущей занятием ведра (если он превышает нагрузку FACOTR, изменение размера в два раза превышает оригинал). При получении объекта мы передаем K, чтобы получить, который вызывает хэшкод для вычисления хэша, чтобы получить позицию в ковше, а также вызывает метод equals (), чтобы определить пару ключевых значений. Если происходит столкновение, HashMap организует элементы, которые создают конфликты столкновений через связанный список. В Java 8, если элементы, которые сталкиваются в ведре, превышают определенный предел (по умолчанию 8), красное и черное дерево используется для замены связанного списка для увеличения скорости.
3. Вы знаете принципы Get and Pult? Каковы функции equals () и hashcode ()?
Хэшируя хэшкод () ключа и вычислить индекс (n-1 и хэш), положение ведер получается. Если происходит столкновение, используйте метод key.equals () для поиска соответствующего узла в связанном списке или дереве
4. Вы знаете реализацию хэша? Зачем мне это делать?
В реализации Java 1.8 он реализован с помощью высокого 16-битного X-или низкого 16-битного hashcode (): (h = k.hashcode ()) ^ (h >>> 16), что в основном рассматривается из скорости, эффективности и качества. Это может гарантировать, что когда N ведра является относительно небольшим, это также может гарантировать, что как высокие, так и низкие биты участвуют в расчете хэша, и не будет чрезмерных накладных расходов.
5. Что если размер HashMap превышает емкость, определенную коэффициентом нагрузки?
Если коэффициент нагрузки превышен (по умолчанию 0,75), HashMap с два раза будет изменен, а метод хэш снова будет вызван.
Шпаргалка о коллекциях Java описывается следующим образом:
Массив хеш -ведра, реализованная с помощью массива входа [], и значения хэша ключа можно использовать для получения подписания массива.
При вставке элемента, если два клавиша попадают в одно и то же ведро (например, после того, как значения хэша 1 и 17 являются модулем 16, оба принадлежат к первому хеш-ведро), вход использует следующую свойство для реализации нескольких записей в одностороннем списке, а также в записи, которая входит в точки кокта рядом с текущей записью ковша.
При поиске ключа со значением хэша 17 сначала найдите первое хеш -ведро, затем вытекайте через все элементы в ведре с связанным списком и сравните их значения ключей один за другим.
Когда количество входа достигает 75% ведра (во многих статьях говорится, что количество используемых ведер достигает 75%, но в соответствии с кодом, массив ведра будет расширена в геометрической прогрессии, и вся исходная запись будет переназначена, поэтому лучше иметь здесь приблизительную ценность.
Операция битов (хэш и (ArrayLength-1)) будет быстрее, поэтому размер массива всегда приходит к мощности N 2. Если вы дадите начальное значение, такое как 17, оно будет преобразовано в 32. Начальное значение по умолчанию, когда элемент первым размещен, составляет 16.
Итератор () проходит вдоль массива ведра хеш -ведра, которая выглядит как вне порядок.
6. Что происходит, когда хешкод двух объектов одинаково?
Поскольку хешкод одинаково, их положение ведра одинакова, и произойдет «столкновение». Поскольку HashMap использует связанный список для хранения объектов, эта запись (объект Map.Entry, содержащий пары клавиш), хранится в связанном списке.
7. Если хешкод двух клавиш одинаково, как вы получаете объект значения?
После поиска локации ведра, метод keys.equals () будет вызван, чтобы найти правильный узел в связанном списке и, наконец, найти объект значения, который будет найден, который будет найден. Следовательно, при разработке ключевого типа HashMap, если используется неизменное объект, и соответствующие методы equals () и hashcode () используются, возникновение столкновений будет снижена, и эффективность будет улучшена. Невыховаемость может кэшировать хешкоды для разных ключей, что увеличит скорость получения всего объекта. Использование классов обертки, таких как String и Interger в качестве клавиш, является очень хорошим выбором.
8. Что если размер HashMap превышает емкость, определенную коэффициентом нагрузки?
Размер коэффициента нагрузки по умолчанию составляет 0,75. То есть, когда карта заполняет 75% ведра, как и другие классы сбора (например, ArrayList и т. Д.), Массив ведра, который в два раза превышает размер исходного HashMap, будет создан для изменения размера карты и поместить исходный объект в новый массив ведра. Этот процесс называется перефразированием, потому что он называет хэш -метод, чтобы найти новое местоположение ведра
9. Вы понимаете, что не так с изменением размера HashMap?
При изменении размера HashMap действительно существует условная конкуренция, потому что, если оба потока обнаруживают, что HashMap необходимо изменить размер, они попытаются изменить размер одновременно. Во время процесса изменения размера порядок элементов, хранящихся в связанном списке, будет изменен, потому что при перемещении в новое положение ведра HashMap не помещает элементы в конце связанного списка, а в голове, который должен избежать прохождения хвоста. Если происходит условное соревнование, то существует порочный цикл. Поэтому в одновременной среде мы используем CurrentHashmap для замены HashMap
10. Почему классы обертки, такие как строка и интернет, подходят в качестве ключей?
Потому что строка неизменна и окончательна, а методы equals () и hashcode () были переписаны. Другие классы обертки также имеют эту функцию. Необходимость необходима, потому что для расчета HashCode () вы должны предотвратить изменение значения ключа. Если значение ключа возвращает другой хешкод при внедрении и получении, вы не можете найти объект, который вы хотите, от HashMap. Необываемость имеет другие преимущества, такие как безопасность потоков. Если вы можете гарантировать, что хэшкод не изменился, просто объявив поле окончательным, пожалуйста, сделайте это. Поскольку методы equals () и hashcode () используются при получении объектов, очень важно правильно переписать эти два метода. Если два неравных объекта возвращают разные хэшкоды, вероятность столкновения будет меньше, что улучшит производительность hashmap
Спасибо за чтение, я надеюсь, что это поможет вам. Спасибо за поддержку этого сайта!