JDK类库源码分析系列3--集合类分析(8) HashMap

90 阅读17分钟

一、结构

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

​ 继承AbstractMap抽象类。HashMap是一种Map其用了3种结构(jdk8)来存储对应的key-value元素,即数组、节点Node,红黑树,同时key-value元素会构建一个Node类。数组用来确定[ 不同元素的hash值与数组长度的且运算 ] (要明白这里,只是计算所得的index相同,而不是这两个元素本身的hash值相同,hash值还是唯一的)来确定对应的位置。

​ 因此这样的话,就会有两个问题:当存储多个元素的话就可能在hash值与数字长度且元素所得到的数组index的位置是相同的,这个时候就需要用到Node类的next属性来讲其放到原来的index位置元素的下面,以此就是一个链了,其实可见将整个链表看成数组中的一个元素、第二个问题就是,我们一直往hashMap中放元素,如果计算的index一直相同,就会放在一个index位置,同时一直往这个index位置的链表一直往后面添加,我们再查询对应元素的时候其实查询的复杂的就可能为n了,所以JDK优化了,当链表的长度超过8(默认值)就将链表结构转换为树(红黑树)结构来存储,这样就能加加快查询速度(我们本次会先跳过红黑树的细节分析,这个到整理算法的时候再解析)。

二、变量

1、DEFAULT_INITIAL_CAPACITY

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

​ HashMap的初始化默认容量大小 16。

2、MAXIMUM_CAPACITY

static final int MAXIMUM_CAPACITY = 1 << 30;

最大的容量大小。

3、DEFAULT_LOAD_FACTOR

static final float DEFAULT_LOAD_FACTOR = 0.75f;

​ 默认的加载因子 (用来计算设置扩容的阈值,例如初始化容量是16 ,然后16*0.75 = 12 ,也就是说当设置)。

4、TREEIFY_THRESHOLD

booleanstatic final int TREEIFY_THRESHOLD = 8;

​ 当链的长度达到此值就将其转换为红黑树。

5、table;

transient Node<K,V>[] table;

​ 存放 [元素hash值与table长度且运算所得的index]第一个计算到该index的元素。

6、entrySet;

transient Set<Map.Entry<K,V>> entrySet;

​ 存放的是HashMap中所有的key-value元素。

7、size;

transient int size;

​ 这个表示当前Map有多少个key-value键值对。

8、modCount

transient int modCount

​ 对Map中元素修改的次数。

9、threshold

int threshold;

​ 扩容的阈值,例如我们前面举例的16*0.72=12,次数这个threshold就是threshold,当前面的size达到12的时候就会触发对table数组的扩容。

10)、loadFactor

final float loadFactor;

​ 加载因子,同前面DEFAULT_LOAD_FACTOR的意义,只是DEFAULT_LOAD_FACTOR表示的是一个具体的值,不过在初始化的时候会将DEFAULT_LOAD_FACTOR赋值给loadFactor。

三、构造方法

1、HashMap(int initialCapacity, float loadFactor)

public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    this.loadFactor = loadFactor;
    this.threshold = tableSizeFor(initialCapacity);
}
/**
 * Returns a power of two size for the given target capacity.
 */
static final int tableSizeFor(int cap) {
    int n = cap - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

​ 这个就是传入初始化容量&扩容加载因子,再通过initialCapacity计算threshold,这里的tableSizeFor元素其是就是获取进为(n + 1),例如你传入7或6,由于右移的对应操作,就会将右边的都变为1,然后再+1就达到了进位,所以就会返回8。

2、HashMap(int initialCapacity)

public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

​ 调用的第一个构造方法,如果加载因子会传入默认的DEFAULT_LOAD_FACTOR。

3、HashMap()

public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

​ 只初始化了loadFactor。

4、HashMap(Map<? extends K, ? extends V> m)

public HashMap(Map<? extends K, ? extends V> m) {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
}

​ 也可以直接传入一个Map来进行初始化。

​ 我们可以看到在1/2/3中与前面的那些集合类有些不同,其并没有对table数组进行对应的初始化。

四、内部类

1、Node

1)、结构&变量&构造方法

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;

    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }

​ 可以看到其实现Map接口所对应的Map.Entry。这个Node就用来存储key-value元素的,可以看到其有四个成员变量:hash-对应key的hash值、key-对应传入的key、value-对应传入的value值、next-此节点的下一个节点(用来处理当多个元素hashCode与数组长度且运算所得的index相同是,这时候就用链表来建立其的关系)。

2)、方法

public final K getKey()        { return key; }
public final V getValue()      { return value; }
public final String toString() { return key + "=" + value; }

public final int hashCode() {
    return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
    V oldValue = value;
    value = newValue;
    return oldValue;
}
public final boolean equals(Object o) {
    if (o == this)
        return true;
    if (o instanceof Map.Entry) {
        Map.Entry<?,?> e = (Map.Entry<?,?>)o;
        if (Objects.equals(key, e.getKey()) &&
            Objects.equals(value, e.getValue()))
            return true;
    }
    return false;
}

​ 可以看到其重写了toString&equals&hashCode方法。在equals方法中其是同时调用key&value两者的equals方法。

2、KeySet

final class KeySet extends AbstractSet<K> {
    public final int size()                 { return size; }
    public final void clear()               { HashMap.this.clear(); }
    public final Iterator<K> iterator()     { return new KeyIterator(); }
    public final boolean contains(Object o) { return containsKey(o); }
    public final boolean remove(Object key) {
        return removeNode(hash(key), key, null, false, true) != null;
    }
    public final Spliterator<K> spliterator() {
        return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
    }
    public final void forEach(Consumer<? super K> action) {
        ......
    }
}

​ 这个是用来放key集合的,其继承AbstractSet,但里面的方法是直接调用的Hashmap的方法。

3、Values

final class Values extends AbstractCollection<V> {
    public final int size()                 { return size; }
    public final void clear()               { HashMap.this.clear(); }
    public final Iterator<V> iterator()     { return new ValueIterator(); }
    public final boolean contains(Object o) { return containsValue(o); }
    public final Spliterator<V> spliterator() {
        return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
    }
    public final void forEach(Consumer<? super V> action) {
        ......
    }
}

​ 这里与上面类似其是继承的AbstractCollection,其是这个与上一篇的AbstractMap类似。

4、EntrySet

final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
    public final int size()                 { return size; }
    public final void clear()               { HashMap.this.clear(); }
    public final Iterator<Map.Entry<K,V>> iterator() {
        return new EntryIterator();
    }
    public final boolean contains(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<?,?> e = (Map.Entry<?,?>) o;
        Object key = e.getKey();
        Node<K,V> candidate = getNode(hash(key), key);
        return candidate != null && candidate.equals(e);
    }
    public final boolean remove(Object o) {
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
            Object key = e.getKey();
            Object value = e.getValue();
            return removeNode(hash(key), key, value, true, true) != null;
        }
        return false;
    }
    ......
}

​ 这个也与前面两个类似,只是EntrySet是用来遍历获取Entry的。这里需要提到的,我们再前面文章是有梳理一些集合的抽象实现的,例如AbstractCollection,其的add方法并没有具体显示,而是需要子类实现,其本身是抛出UnsupportedOperationException异常。

public boolean add(E e) {
    throw new UnsupportedOperationException();
}

​ 所以其实我们主要是用这些EntrySet、Values的迭代器去遍历其的值,而并不能进行添加操作(这样操作也不符号节点的添加逻辑)。

5、HashIterator

1)、结构&成员变量&构造方法

abstract class HashIterator {
    Node<K,V> next;        // next entry to return
    Node<K,V> current;     // current entry
    int expectedModCount;  // for fast-fail
    int index;             // current slot

    HashIterator() {
        expectedModCount = modCount;
        Node<K,V>[] t = table;
        current = next = null;
        index = 0;
        if (t != null && size > 0) { // advance to first entry
            do {} while (index < t.length && (next = t[index++]) == null);
        }
    }

​ 对Node节点遍历的的迭代器实现。可以看到在其的初始化方法中,通过do-while遍历按顺序找到table数组的最前面的一个node节点赋值给next,因为计算hashCode 对应的index是散列的。要注意这里的index变量,其是表示的当前遍历到了table数组的那个槽位。

2)、方法

public final boolean hasNext() {
    return next != null;
}

final Node<K,V> nextNode() {
    Node<K,V>[] t;
    Node<K,V> e = next;
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    if (e == null)
        throw new NoSuchElementException();
    if ((next = (current = e).next) == null && (t = table) != null) {
        do {} while (index < t.length && (next = t[index++]) == null);
    }
    return e;
}

public final void remove() {
    Node<K,V> p = current;
    if (p == null)
        throw new IllegalStateException();
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    current = null;
    removeNode(p.hash, p.key, null, false, false);
    expectedModCount = modCount;
}

​ 我们看下nextNode方法,其是通过next这个节点遍历,如果其后面next还有其它的节点则往下,如果此index 对应的链表遍历完了,则再进行index++到下一个槽位,以此来完成对所有节点的遍历。而remove方法则是直接调用的HashMap的方法。

6、KeyIterator

final class KeyIterator extends HashIterator
    implements Iterator<K> {
    public final K next() { return nextNode().key; }
}

​ 迭代器对节点的key遍历获取。

7、ValueIterator

final class ValueIterator extends HashIterator
    implements Iterator<V> {
    public final V next() { return nextNode().value; }
}

​ 迭代器对节点的value遍历获取。

8、EntryIterator

final class EntryIterator extends HashIterator
    implements Iterator<Map.Entry<K,V>> {
    public final Map.Entry<K,V> next() { return nextNode(); }
}

​ 对节点(Entry | Node)进行遍历,可以看到其只是将nextNode包装为next方法。

9、TreeNode

static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
    TreeNode<K,V> parent;  // red-black tree links
    TreeNode<K,V> left;
    TreeNode<K,V> right;
    TreeNode<K,V> prev;    // needed to unlink next upon deletion
    boolean red;
    TreeNode(int hash, K key, V val, Node<K,V> next) {
        super(hash, key, val, next);
    }

​ 这里还有红黑树的实现,不过这个我们到以后整理算法相关的内容在来分析。同时这里这些集合类都是由流操作的,不过这个我们下一个系列补上。两个大坑

五、方法

1、hash(Object key)

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

​ 这里就是计算key对应的hashCode的值。可以看到其是对原来key的hashCode再进行了一次异或操作。

2、put(K key, V value)

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

​ 将key-value对应的元素添加到map中。

3、newNode

Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
    return new Node<>(hash, key, value, next);
}

​ 根据hash值&key&value以及其的下一个节点创建一个对应的Node节点

4、afterNodeAccess&afterNodeInsertion&afterNodeRemoval

// Callbacks to allow LinkedHashMap post-actions
void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
void afterNodeRemoval(Node<K,V> p) { }

​ 这3个方法是空的其是用来给HashMap的这里linkedHashMap实现操作的。

5、resize()

final Node<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;
        }
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    }
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        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"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[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)
                    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;
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

​ 这是对当前数据进行扩容,是HashMap中一个重要方法,我们来具体分析下。

​ 1)、首先是将当前数组table的长度赋值给oldCap表示以前数组的容量

int oldCap = (oldTab == null) ? 0 : oldTab.length;

​ 2)、再将扩容的阈值threshold赋值给oldThr表示原来的扩容阈值

int oldThr = threshold;

​ 3)、再定义新的容量&阈值变量newCapnewThr

int newCap, newThr = 0;

​ 4)、然后再判断原来的容量oldCap进行对应赋值

if (oldCap > 0) {
    if (oldCap >= MAXIMUM_CAPACITY) {
        threshold = Integer.MAX_VALUE;
        return oldTab;
    }
    else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
             oldCap >= DEFAULT_INITIAL_CAPACITY)
        newThr = oldThr << 1; // double threshold
}

​ 这里的oldCap如果>0表示已经对table数组进行初始化了,这里首先是判断原来容量oldCap是否已经达到最大值了,如果>=MAXIMUM_CAPACITY MAXIMUM_CAPACITY = 1 << 30了,则本次将扩容阈值threshold变为Integer的最大值,然后返回原数组。不然将原来的容量左移扩大为原来的两倍赋值给newCap新容量,同时newThr也在原来的基础上将其左移位。

​ 5)、如果原来的oldThr扩容阈值大于0&oldCap不大于0(需要不满足前面的if条件),则将原来的扩容阈值作为本次的容量。(构造方法的选择会出现这种情况)

else if (oldThr > 0) // initial capacity was placed in threshold
    newCap = oldThr;

​ 6)、上面两种都不满足就是下面这种逻辑,容量设置的默认容量,扩容阈值也是用的默认的加载因子DEFAULT_LOAD_FACTOR 0.75

else {               // zero initial threshold signifies using defaults
    newCap = DEFAULT_INITIAL_CAPACITY;
    newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}

​ 7)、如果newThr经过上面逻辑为0(例如走的上面的else if逻辑),则再计算其值计算float ft = (float)newCap * loadFactor;,不过这里是不是可以直接提到else if中?看上面的赋值其他的一般不会造成newThr为0?

​ 8)、将计算所得的newThr赋值给threshold,再根据新的newCap创建对应数组,并赋值给成员变量table

threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;

扩容后需要进行对应订单节点转移查询分配对应的位置。不过这里的前提是if (oldTab != null)以前的table数组是初始化了的,然后遍历以前的容量长度oldCap获取数据重新分配

​ 9)、遍历看该index位置是否有值e = oldTab[j]) != null并赋值给e,有值则进行处理

if (oldTab != null) {
    for (int j = 0; j < oldCap; ++j) {
        Node<K,V> e;
        if ((e = oldTab[j]) != null) {

​ 10)、将原来该位置置为null(该位置节点的内容已经赋值给e了),如果e元素其没有next下一个元素了表明该位置只有一个Node,则通过e.hash & (newCap - 1)计算新的槽位设置到newTable中。

oldTab[j] = null;
if (e.next == null)
    newTab[e.hash & (newCap - 1)] = e;

​ 11)、如果该e的next有另外的节点,则先判断该节点是否已经由简单点的链表转换为了TreeNode红黑树了,是的话就去使用TreeNode方法的去处理(这里我们跳过)。

else if (e instanceof TreeNode)
    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);

​ 12)、如果是普通的链表就走该逻辑

else { // preserve order

​ 13)、这一块的逻辑主要是需要明白其的四个局部变量:loHead-低位的头节点、loTail低位的尾节点、hiHead-高位的头节点、hiTail-高位的尾结点。

​ 现在我们来说明下这个高位、低位:首先我们假设原来table数组长度是4二进制是100(index槽位为 0-3,所以我们可以看到前面在进行key.hashCode与数组长度的且运算是需要对数组长度进行减1操作的,即对应的二进制是11),现在对table扩容由4变为81000,此时的index就是0-7 111。我们假设两个key的hash值是00110111,这两个key在原来容量4对应的11与其进行且运算都是11,index为3。现在扩容了,容量变为8 对应的最大index是111,这个时候0011的且运算就是311,而0111的且运算就变为了7111,同时7=原来index3+原来的容量4,重新计算的3就是本次的低位、7就是本次计算的高位。所以如果原来容量4形成的链表其的Node在第3位有些为1或0,在新的容量的index 3与index 7会形成两个新的链表,这两个链表会有自己的head节点、tail节点。

Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
    next = e.next;
    if ((e.hash & oldCap) == 0) {
        if (loTail == null)
            loHead = e;
        else
            loTail.next = e;
        loTail = e;
    }
    else {
        if (hiTail == null)
            hiHead = e;
        else
            hiTail.next = e;
        hiTail = e;
    }
} while ((e = next) != null);
if (loTail != null) {
    loTail.next = null;
    newTab[j] = loHead;
}
if (hiTail != null) {
    hiTail.next = null;
    newTab[j + oldCap] = hiHead;
}

​ 上面代码loHead&loTail,就是描叙形成的低位链表,可以看到在后面的赋值是if (loTail != null) {newTab[j] = loHead;

hiHead&hiTail是描叙的高位,其的赋值是if (hiTail != null) {newTab[j + oldCap] = hiHead;。同时这里判断进位的高低位是通过if ((e.hash & oldCap) == 0),判断,例如我们有4扩容到8,这里的oldCap就是4100,将其与hash且运算就能判断第3位是否为1了,以此就能区分高低位了。

​ 到这里扩容后的元素重新分配就完成了。

6、putVal(...)

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

​ 这个方法就是将key-value元素放到map中。

​ 1)、实现判断table数组是否完成初始化化创建,如果没有创建则通过resize方法去创建,n表示数组长度,i表示table的index。

Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
    n = (tab = resize()).length;

​ 2)、table完成初始化创建后(如果需要),通过i = (n - 1) & hash]计算本次添加的index位置(并将其赋值给p),如果该位置还没有值,则通过newNode方法创建一个Node节点添加在该位置。如果该位置有值了,则是另外的处理了。

if ((p = tab[i = (n - 1) & hash]) == null)
    tab[i] = newNode(hash, key, value, null);

​ 3)、判断前面赋值的index位置节点p的hash值是否等于本次添加的元素的hash值&key相等,如果相等,将其赋值给e(表示已经存在该节点了),这个e表示的是根据key-value创建的本次将要添加的Node节点。

Node<K,V> e; K k;
if (p.hash == hash &&
    ((k = p.key) == key || (key != null && key.equals(k))))
    e = p;

​ 4)、如果当前节点已经变为了TreeNode,则是用TreeNode去处理

else if (p instanceof TreeNode)
    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

​ 5)、如果是普通链表,则是循环链表找到对应位置进行添加

else {
    for (int binCount = 0; ; ++binCount) {
        if ((e = p.next) == null) {
            p.next = newNode(hash, key, value, null);
            if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                treeifyBin(tab, hash);
            break;
        }
        if (e.hash == hash &&
            ((k = e.key) == key || (key != null && key.equals(k))))
            break;
        p = e;
    }
}

​ 遍历链表,看其有没有下一个,同时还通过hash&key判断再这个链表中有没有对应的节点,像前面的判断一样,如果有则不需要创建新的节点,直接break。如果next没有了,通过newNode创建对应的节点赋值添加给p,添加到此index位置链表的最后面,同时如果已经超过TREEIFY_THRESHOLD - 1了,则将该链表转换为TreeNode。

​ 6)、如果e!= null,表示已经存在该节点了,这里先判断如果是只有存在才添加,就不用新的value来替代原来的value,如果不是就替代(这里如果原来是null即使是存在才添加也会添加),然后调用afterNodeAccess方法,最后将原来的值返回。

if (e != null) { // existing mapping for key
    V oldValue = e.value;
    if (!onlyIfAbsent || oldValue == null)
        e.value = value;
    afterNodeAccess(e);
    return oldValue;
}

​ 7)、修改+1、size加1,如果size已经达到了扩阈值threshold,则扩容,然后调用afterNodeInsertion方法。

++modCount;
if (++size > threshold)
    resize();
afterNodeInsertion(evict);
return null;

7、getNode(int hash, Object key)

final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        if ((e = first.next) != null) {
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}

​ 根据hash&key获取对应Node的value。这里先是通过(n - 1) & hash找到对应的index的第一个节点,在遍历判断hash&key是否相等。

8、get(Object key)

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

​ 根据key获取对应节点的值,可以看到这里是调用的getNode方法,同时hash传入的是通过hash方法再进行key的hash值。

9、containsKey(Object key)

public boolean containsKey(Object key) {
    return getNode(hash(key), key) != null;
}

​ 通过getNode方法看获取的value是不是null。

10、size()

public int size() {
    return size;
}

​ 已经存放了多少key-value键值对了。

11、removeNode(...)

final Node<K,V> removeNode(int hash, Object key, Object value,
                           boolean matchValue, boolean movable) {
    Node<K,V>[] tab; Node<K,V> p; int n, index;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (p = tab[index = (n - 1) & hash]) != null) {
        Node<K,V> node = null, e; K k; V v;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            node = p;
        else if ((e = p.next) != null) {
            if (p instanceof TreeNode)
                node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
            else {
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key ||
                         (key != null && key.equals(k)))) {
                        node = e;
                        break;
                    }
                    p = e;
                } while ((e = e.next) != null);
            }
        }
        if (node != null && (!matchValue || (v = node.value) == value ||
                             (value != null && value.equals(v)))) {
            if (node instanceof TreeNode)
                ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
            else if (node == p)
                tab[index] = node.next;
            else
                p.next = node.next;
            ++modCount;
            --size;
            afterNodeRemoval(node);
            return node;
        }
    }
    return null;
}

​ 这一块就是删除对应节点,matchValue入参是表示进行节点删除的时候,节点相等判断不单是要判断hash&key,还要判断value,另一个movable是用于TreeNode的。这里先根据index = (n - 1) & hash获取index位置的第一个Node。

​ 1)、先判断第一个node是否已经相等了,如果是则将其赋值给node(node = p)

if (p.hash == hash &&
    ((k = p.key) == key || (key != null && key.equals(k))))
    node = p;

​ 2)、(省略了TreeNode部分),遍历判断,找到对应的Node,这里要注意如果找到了是直接break,只有node = e;,没有p = e;,表示其不是第一个元素。

else {
    do {
        if (e.hash == hash &&
            ((k = e.key) == key ||
             (key != null && key.equals(k)))) {
            node = e;
            break;
        }
        p = e;
    } while ((e = e.next) != null);
}

​ 3)、上面提到的没有p = e也就是node != p,表明其不是第一个元素,就是直接将前一个节点p的next去连接找到的对应node的next,这样就直接删除了node。如果node == p,表示是第一个,则直接是node.next(其为null)赋值给tab[index],来达到删除node。

if (node != null && (!matchValue || (v = node.value) == value ||
                     (value != null && value.equals(v)))) {
    if (node instanceof TreeNode)
        ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
    else if (node == p)
        tab[index] = node.next;
    else
        p.next = node.next;
    ++modCount;
    --size;
    afterNodeRemoval(node);
    return node;
}

12、remove(Object key)

public V remove(Object key) {
    Node<K,V> e;
    return (e = removeNode(hash(key), key, null, false, true)) == null ?
        null : e.value;
}

​ 删除key对应的node,其会返回node对应的value,调用的removeNode方法。

13、clear()

public void clear() {
    Node<K,V>[] tab;
    modCount++;
    if ((tab = table) != null && size > 0) {
        size = 0;
        for (int i = 0; i < tab.length; ++i)
            tab[i] = null;
    }
}

​ 清空,可以看到直接简单粗暴的进行tab[i] = null;

14、containsValue(Object value)

public boolean containsValue(Object value) {
    Node<K,V>[] tab; V v;
    if ((tab = table) != null && size > 0) {
        for (Node<K, V> e : tab) {
            for (; e != null; e = e.next) {
                if ((v = e.value) == value ||
                    (value != null && value.equals(v)))
                    return true;
            }
        }
    }
    return false;
}

​ 是不是含有这个value。直接遍历的table数组。

15、keySet()

public Set<K> keySet() {
    Set<K> ks = keySet;
    if (ks == null) {
        ks = new KeySet();
        keySet = ks;
    }
    return ks;
}

​ key对应集合

16、values()

public Collection<V> values() {
    Collection<V> vs = values;
    if (vs == null) {
        vs = new Values();
        values = vs;
    }
    return vs;
}

​ value值集合

17、entrySet()

public Set<Map.Entry<K,V>> entrySet() {
    Set<Map.Entry<K,V>> es;
    return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}

​ 所有Node节点集合。

18、capacity()

final int capacity() {
    return (table != null) ? table.length :
        (threshold > 0) ? threshold :
        DEFAULT_INITIAL_CAPACITY;
}

​ 容量获取。