Предисловие: после Java 8 добавлено много новых вещей. Я нашел некоторую соответствующую информацию в Интернете. После того, как HashMap подвергся насилию, я решил разобраться в соответствующих знаниях для себя. Эта статья для справки на картинке и некоторых контента: http://www.vevb.com/article/80446.htm
Структура хранения HashMap показана на рисунке: если на ведре более 8 узлов, структура хранения представляет собой красное и черное дерево, а менее 8-односторонний список.
1: некоторые свойства HashMap
Hashmap public Class <K, v> Extrable Map <K, V> реализует Map <K, V>, Clonable, Serializable {Private Static Long Long SerialversionUID = 362498820763181265L; // По умолчанию исходная способность - 16 -й конечная финальная intative intatie_capice = 1 << 4; 30; // Коэффициент заполнения по умолчанию (предыдущие версии также назывались коэффициентом загрузки) Статический окончательный поток default_load_factor = 0,75F; // Это порог. Когда количество связанных списков на ведре больше, чем это значение, оно будет преобразовано в красное и черное дерево. Код метода PUT использует статический окончательный int vreeify_threshold = 8; // Это также порог. Когда количество связанных списков в ведре меньше, чем это значение, дерево преобразуется в связанный список статического конечного конечного окончания int untreeify_throshold = 6; // Сказано в комментарии исходного кода, что: минимальная емкость дерева, по крайней мере, 4 x reteify_threshold = 32. Массив элементов сохраняется, всегда кратный 2 переходного узла <K, V> [] Таблица; Transient Set <Map.Entry <K, v >> intrySet; // Количество хранимых элементов, обратите внимание, что это не равно длине массива. Transient int size; // счетчик для каждого расширения и изменение структуры карты - Transient int modcount; // Критическое значение расширяется, когда фактический размер (коэффициент емкости * заполнения) превышает критическое значение, емкость расширяется int threshold; // Коэффициент заполнения окончательный платный нагрузочный фактор;2: метод строительства HashMap
// Конструктор для определения начальной емкости и коэффициента заполнения общедоступной Hashmap (int initialCapency, float loadFactor) {// Указанная начальная емкость не является неотрицательной if (начальная капсула <0) Выбросить новый нелегалргментальный экзамен (незаконная начальная емкость: +начальная капсула); // Если указанная начальная емкость больше, чем максимальная емкость, максимальная емкость, если (maximum емкость, если (максимальная емкости, максимальная емкость, если (максимальная емкость>, максимальная емкости,> начальная емкость, если (максимальная емкость>, максимальная емкость>, на начальной. Maximum_capacity; // Коэффициент заполнения является положительным, если (loadfactor <= 0 || float.isnan (loadfactor)) выбросить новое allodalargumentException (нелегальная нагрузка коэффициента: +loadfactor); this.LoadFactor = LoadFactor; // После определения емкости метод TablesIze для расчета критического значения. Если значение превышается при размещении данных, оно будет расширяться. Значение определенно кратно 2./// Указанная начальная емкость не была сохранена, и оно используется только для генерации критического значения This.Threshold = tableSizefor (initialCapacity);} // Этот метод гарантирует, что он всегда возвращает значение больше, чем CAP, и множественное 2. n | = n >>> 1; n | = n >>> 2; n | = n >>> 4; n | = n >>> 8; n | = n >>> 16; // Вложенное возврат тригонометрических операторов (n <0)? 1: (n> = maximum_capacity)? Maximum_capacity: n + 1;} // constructor 2public hashmap (int initialCapacity) {this (initialCapacity, default_load_factor);} // Конструктор 3Public hashmap () {this.loadfactor = default_load_factor; // все остальные поля не выполнены}3: Определите положение элемента в массиве при получении и положении
Статический окончательный int hash (object key) {int h; return (key == null)? 0: (h = key.hashcode ()) ^ (h >>> 16);}Чтобы определить местоположение
Первый шаг: первое, что нужно вычислить хэш -код ключа, который является номером типа Int. В следующих комментариях H >>> 16 Комментарий исходного кода говорится: Чтобы избежать хеш -столкновений, высокая позиция распространяется в низкую позицию, которая сделана после учета различных факторов, таких как скорость и производительность.
Шаг 2: h-хэш-код, длина-это длина узла [] массива выше, выполняйте ту же работу H & (длина-1). Поскольку длина кратна 2 -1, его двоичный код составляет 1, а результат 1 с другими числами на нем может быть 0 или 1, чтобы обеспечить однородность после операции. То есть метод хэша гарантирует единообразие результатов, что очень важно и сильно повлияет на то, чтобы получить и получить производительность HashMap. Смотрите следующую картинку для сравнения:
Рисунок 3.1 - асимметричные результаты хэша
Рисунок 3.2 - сбалансированный хэш -результат
На этих двух графиках не так много данных. Если связанный список длиннее 8, он будет преобразован в красное и черное дерево. Это было бы более очевидно в то время. JDK8 всегда был связанным списком раньше. Сложность связанного запроса списка - O (n), а сложность красных и черных деревьев из -за их собственных характеристик - O (log (n)). Если результаты хэша неровные, это значительно повлияет на сложность операции. Существует <a href = "http://blog.chinaunix.net/uid-26575352-id-3061918.html"> Блог базовых знаний красного и черного дерева </a> Существует еще один пример, чтобы проверить: настраиваемый объект используется для создания ключей и корректировки метода Hashcode (), чтобы увидеть, что Pul стоит.
Общедоступный класс mitableKeyTest {public static void main (string args []) {class mykey {integer i; public void seti (integer i) {this.i = i;} public mykey (Integer i) {this.i = i;}@overridepublic int hashcode () {// если 1 // return 1retr at ateme as ecoed a evilord i; Должен быть реализован @OverridePublic Boolean Equals (Object obj) {if (obj exanceOf mykey) {return i.eecalles ((((myKey) obj) .i);} els = new Hashmap <> (25000,1); Date Begin = new Date (); для (int i = 0; i <20000; i ++) {map.put (new mykey (i), "test" + i);} date end = new Date (); System.out.println ("Time (ms)" + (end.gettime () - getse.gettime ());4: Получить метод
public v get (object key) {node <k, v> e; return (e = getNode (hash (key), key)) == null? null: e.value;} окончательный узел <k, v> getNode (int hash, объект клавиша) {node <k, v> [] tab; Узел <K, V> Сначала, E; int n; K K; // hash & (length -1) Получите корневую позицию красного и черного дерева или заголовок связанного списка if ((tab = table)! = Null && (n = tab.length)> 0 && (first = tab [n - 1) & hash])! = Null) {if (first.hash == hash &&/ alway key.equals (k)))) вернуть сначала; if ((e = first.next)! = null) {// Если это дерево, сложность переселения красного и черного дерева - o (log (n)), чтобы получить значение узла, если (первый экземпляр TREENODE) return ((treeNode <K, v>). Сначала). hash && ((k = e.key) == key || (key! = null && key.equals (k)))) return e;} while ((e = e.next)! = null);}} return null;}5: Поместите метод, когда положите, найдите ведро в соответствии с H & (Длина 1), а затем посмотрите, является ли это красным и черным деревом или связанным списком и путтвалом
public v put (k key, v value) {return putval (hash (key), key, value, false, true);} final v putval (int hash, k key, v value, boolean onayifabsent, boolean evict) {node <k, v> [] Tab; Узел <K, V> P; int n, i; // Если вкладка пуста или длина 0, память выделена resize () if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize ()). Length; // (n - 1) & Hash находит положение пута. Если это пусто, то PUTIF напрямую ((p = вкладка [i = (n - 1) & hash]) == null) вкладка [i] = newNode (хэш, ключ, значение, null); else {node <k, v> e; K K; // Значение хэша первого узла одинаково, а значение ключа такое же, как и ключ вставки if (p.hash == hash && ((k = p.key) == key || (key! = Null && key.equals (k)))) e = p; else (p encementOf treeNode) // пункт красно -черного дерева относительно осложняется. После Путвала вы должны пройти все дерево. При необходимости измените значение, чтобы обеспечить характеристики красного и черного дерева e = ((treeNode <K, v>) p). Puttreeval (this, tab, hash, key, value); else {// связанный список для (int bincount = 0;; ++ bincount) {if (e = p.next) = nul Конец таблицы, затем создайте новый узел p.next = newnode (хэш, ключ, значение, null); // после добавления нового узла, если количество узлов достигает порога, преобразуйте связанный список в красное и черное дерево, если (bincount> = reareify_threshold - 1) // -1 для 1sttreebinebin (tabount>; hash && ((k = e.key) == key || (key! = null && key.equals (k)))) разрыв; p = e;}} // Обновление значения узла с тем же значением хэша и значением ключа if (e! value; jouseodeAccess (e); return oldValue;}} ++ modCount; if (++ size> threshold) resize (); whentodeinsertion (evict); return null;}6: Изменение размера метод
Окончательный узел <k, v> [] resize () {node <k, v> [] oldtab = table; int oldcap = (oldtab == null)? 0: oldtab.length; int oldthr = threshold; int newcap, newthr = 0; if (oldcap> 0) {if (oldcap> = maximum_capacity) {threshold = integer.max_value; return oldTab;} // Столко Maximum_capacity && oldcap> = default_initial_capacity) newthr = oldthr << 1; // двойной порог} else if (oldthr> 0) // Начальная емкость была размещена в thresholdnewcap = oldThr; else {// нулевой начальный порог означает использование defaultsnewcap = default_initial_capacity; newTh loadfactor; newthr = (newcap <maximum_capacity && ft <(float) maximum_capacity? (int) ft: integer.max_value);} threshold = newTh Узел [newCap]; Table = newtab; 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) null) newtab [e.hash & (newc & (j] = null; if (e.next = null) [e.hash & (j] = null; if e (e.next = null). TREENODE) ((TreeNode <K, V>) E) .split (this, newtab, j, oldcap); else {// сохранить ordernode <k, v> loehead = null, lotile = nul (lotail == null) loehead = e; elselotail.next = e; lotail = e;} else {if (Hitail == null) hihead = e; elsehitail.next = e; witail = e;}} while ((e = next)! = null); if (lotail! = null) {lotail.next = null; null) {witail.next = null; newtab [j + oldcap] = hihead;}}}} return newtab;}Выше приведено соответствующие знания об анализе принципа реализации Java8 HashMap, введенного вам редактором. Я надеюсь, что это будет полезно для вас!