Понимание того, как работает Java HashMap - PullRequest
0 голосов
/ 21 марта 2020

Я знаю следующее о Java HashMap put():

  1. Вызов put() начинается с вставки значений ключа в HashMap.
  2. Изначально мы связали список для bin, но как только мы имеем больше, чем `TREEFY_THRESHOLD записей в bin, содержимое bin реорганизуется из связанного списка в сбалансированные деревья.

Аналогично, get() должен работать следующим образом:

  1. Если корзина, в которой хранится ключ, находится в форме связанного списка, просмотрите его, чтобы проверить, существует ли входной ключ в списке или нет. Если это так, то верните свое значение.
  2. Если корзина находится в сбалансированном дереве формы, найдите ключ в дереве и верните его значение

Это абстрактное понимание очень высокого уровня после прочтения некоторых статей, но я не могу увидеть, как именно это происходит в коде. Я попытался немного погуглить и нашел эти хорошие статьи: 1 , 2 , 3 . Но они нацелены на HashMap с java 7 (который не включает сбалансированные логики деревьев c), а не java 8 (который включает сбалансированные логики деревьев c). На самом деле я борюсь не со сбалансированными логиками деревьев c, а с остальными. Реализация Java 7 была довольно простой, но не реализация java 8. Весь код довольно сложен и труден для понимания. Будет здорово, если кто-нибудь объяснит важные методы HashMap. Если нет, то ниже приведены некоторые конкретные c сомнения:

  1. Источник Java 8 HashMap.get() :

    1.     public V get(Object key) {
    2.         Node<K,V> e;
    3.         return (e = getNode(hash(key), key)) == null ? null : e.value;
    4.     }
    5. 
    6.     /**
    7.      * Implements Map.get and related methods
    8.      *
    9.      * @param hash hash for key
    10.      * @param key the key
    11.      * @return the node, or null if none
    12.      */
    13.     final Node<K,V> getNode(int hash, Object key) {
    14.         Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    15.         if ((tab = table) != null && (n = tab.length) > 0 &&
    16.             (first = tab[(n - 1) & hash]) != null) {
    17.             if (first.hash == hash && // always check first node
    18.                 ((k = first.key) == key || (key != null && key.equals(k))))
    19.                 return first;
    20.             if ((e = first.next) != null) {
    21.                 if (first instanceof TreeNode)
    22.                     return ((TreeNode<K,V>)first).getTreeNode(hash, key);
    23.                 do {
    24.                     if (e.hash == hash &&
    25.                         ((k = e.key) == key || (key != null && key.equals(k))))
    26.                         return e;
    27.                 } while ((e = e.next) != null);
    28.             }
    29.         }
    30.         return null;
    31.     }
    

    (a) Зачем всегда явно проверять первый узел в строке 17?
    (b) Что делает tab[(n - 1) & hash] в строке 16?
    (c) Может мне понадобится понимание hash() источник также:

    static final int hash(Object key) {
       int h;
       return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    
  2. Java 8 HashMap.put() источник :

    1.     public V put(K key, V value) {
    2.         return putVal(hash(key), key, value, false, true);
    3.     }
    4. 
    5.     final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
    6.                    boolean evict) {
    7.         Node<K,V>[] tab; Node<K,V> p; int n, i;
    8.         if ((tab = table) == null || (n = tab.length) == 0)
    9.             n = (tab = resize()).length;
    10.         if ((p = tab[i = (n - 1) & hash]) == null)
    11.             tab[i] = newNode(hash, key, value, null);
    12.         else {
    13.             Node<K,V> e; K k;
    14.             if (p.hash == h ash &&
    15.                 ((k = p.key) == key || (key != null && key.equals(k))))
    16.                 e = p;
    17.             else if (p instanceof TreeNode)
    18.                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
    19.             else {
    20.                 for (int binCount = 0; ; ++binCount) {
    21.                     if ((e = p.next) == null) {
    22.                         p.next = newNode(hash, key, value, null);
    23.                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
    24.                             treeifyBin(tab, hash);
    25.                         break;
    26.                     }
    27.                     if (e.hash == hash &&
    28.                         ((k = e.key) == key || (key != null && key.equals(k))))
    29.                         break;
    30.                     p = e;
    31.                 }
    32.             }
    33.             if (e != null) { // existing mapping for key
    34.                 V oldValue = e.value;
    35.                 if (!onlyIfAbsent || oldValue == null)
    36.                     e.value = value;
    37.                 afterNodeAccess(e);
    38.                 return oldValue;
    39.             }
    40.         }
    41.         ++modCount;
    42.         if (++size > threshold)
    43.             resize();
    44.         afterNodeInsertion(evict);
    45.         return null;
    46.     }
    

    (a) Я понимаю putTreeVal() в строке 18 вставляет ключ- значение в сбалансированном дереве. Но я не нахожу logi c для помещения значения ключа в связанный список, как в строке 27 getNode(), что включает в себя обход до конца связанного списка.
    (b) Что делает if в строках 10 и 14 делать?
    (c) Я также не получаю полный лог c полностью ясно. Можете ли вы предоставить комментарии для каждой важной строки. На самом деле я хочу знать, есть ли какая-либо репозитория документации / исходного кода с комментариями к каждой важной строке, объясняющая, что он пытается понять, потому что в этом классе есть много сложных сложных методов, таких как resize(), tableSizeFor(int)

...