HashMap

HashMap是基于哈希表的Map接口的实现。不能保证Node的顺序。允许key和value为null。当然key是不能重复的。

继承的类

  • AbstractMap 提供了一些Map基本实现的抽象类。

实现的接口

  • Map 声明了一些常用的方法。
  • Cloneable 对象克隆标记接口。
  • Serializable 序列化接口。

静态内部类Node

Node实现了Map.Entry接口。所以在本文中。Node和entry是一个东西。在jdk1.7中叫entry。

属性

  • final int hash 当前Node的key的哈希码。是不可修改的。
  • final K key 当前Node的key。是不可修改的
  • V value 当前Node的value。
  • Node<K,V> next 当前Node指向的的下一个Node

方法

  • getKey 获取当前Node的key。
  • getValue 返回当前Node的value。
  • setValue 修改当前Node的value。
  • equals 将当前Node与给定对象进行比较。
  • hashCode 获取当前Node的key-value的哈希码Objects.hashCode(key) ^ Objects.hashCode(value)

内部类KeySet

继承自抽象类AbstractSet,是要是提供了一些操作keySet的方法。

方法

  • size 返回key的数量。
  • clear 清空map中的所有Node。
  • iterator 获取keySet的迭代器。
  • contains 判断keySet中是否包含给定key。
  • remove 将给定key对象的Node从map中删除。
  • spliterator获取keySet的可分割迭代器。
  • forEach为所有的key执行给定的action操作。

内部类Values

继承了AbstractCollection抽象类,主要是提供一些操作values的方法

与上面的KeySet类似,但是Values没有remove`方法。

内部类EntrySet

继承自AbstractSet,主要是提供一些操作Node的方法。

  • size 返回Node的数量。
  • clear 清空map中所有的Node。
  • iterator 获取entrySet的迭代器。
  • contains判断entrySet是否包含给定的Node
  • remove 删除给定的Node。
  • spliterator 返回Node的可分割迭代器。
  • forEach 为所有的Node执行给定action操作。

抽象内部类HashIterator

获取遍历map的迭代器。

属性

  • next map中当前entry的下一个entry。
  • current 当前Node
  • expectedModCount 记录当前的expectedModCount,用来进行快速失败。
  • index 当前位于数组的位置。

构造方法

这是在初始化上面的四个成员变量。

current默认是null,index默认是0,expectedModCount是调用当前构造其时map的modCount

HashIterator() {
    expectedModCount = modCount;
    Node<K,V>[] t = table;
    current = next = null;
    index = 0;
    if (t != null && size > 0) { // advance to first entry
        // 这里是在遍历table,找出数组中index后面不为null的元素
        do {} while (index < t.length && (next = t[index++]) == null);
    }
}

方法

  • boolean hasNext() 判断是否存在下一个Node。

  • Node<K,V> nextNode() 返回下一个Node(当前方法是不能被重写的)

    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();
        // 这里会去当前Node所在的链表上去找。看他的下一个节点是否为null
        if ((next = (current = e).next) == null && (t = table) != null) {
            // 跟构造方法中的一样。去遍历table找到下一个不为null的元素。
            do {} while (index < t.length && (next = t[index++]) == null);
        }
        return e;
    }
    
  • void remove() 删除current,这里最终调用的map的removeNode删除的。

KeyIteratorValueIteratorEntryIterator

三个类都继承了HashIterator,并且实现了Iterator接口。

三个类都不能被继承。

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

静态内部类TreeNode

该类本质上是继承了HashMap中的Node类。虽然表面上看到的是继承了LinkedHashMap.Entry<K,V>,但是LinkedHashMap.Entry<K,V>是继承了HashMap.Node<K,V>的。

TreeNodeNode的另一种形式。当map中table数组的某一处链表长度>=8时,并且table.length >= 64时,会将链表转为红黑树。此时Node就要升级成TreeNode。(如果table.length <64进对table进行扩容)

成员变量

  • static final int DEFAULT_INITIAL_CAPACITY = 1 << 4 默认初始容量为16,必须是2的幂。
  • static final int MAXIMUM_CAPACITY = 1 << 30; 最大容量2的30次幂。
  • static final float DEFAULT_LOAD_FACTOR = 0.75f 默认负载系数。
  • static final int TREEIFY_THRESHOLD = 8; 决定使用树替换链表的阀值。当链表的深度大于8时并且当前table.lenght>=64时,将链表转为红黑树。
  • static final int UNTREEIFY_THRESHOLD = 6 决定了使用链表替换树的阀值。如果
  • static final int MIN_TREEIFY_CAPACITY = 64是扩容还是使用树替换链表的阀值。64值的是table.length,一般都是与TREEIFY_THRESHOLD一起判断
  • transient Node<K,V>[] table 存放Node的数组。默认是null。每次扩容都是原来的2倍(oldCap<<1)
  • transient Set<Map.Entry<K,V>> entrySet 存放Node的Set集合。默认是null。
  • transient int size map中Node的数量。
  • transient int modCount 记录了当前map的修改次数。主要用来做fail-fast
  • int threshold 要调整table大小的阀值(计算方式table.length * DEFAULT_LOAD_FACTOR)。当map的size大于了threshold,就对table进行扩容。
  • final float loadFactor 当前map的负载系数,负责控制map何时扩容。

构造方法

这里所有的构造方法都不回去初始化table,也就是说创建的map对象默认容量是0。table的初始化是在第一次调用put方法时做的。

构造方法只会值设置map的负载系数loadFactor和要调整table大小的阀值threshold

创建一个map一般我们最多只需要确定thresholdloadFactor

HashMap(int initialCapacity, float loadFactor)

给定了初始容量和负载系数的构造器。

给定的initialCapacity如果不是2的n次幂,就换将他转为比他大的2的n次幂。比如initialCapacity=20,最后计算出的threshold=32

tableSizeFor(int cap)将给定的cap的二进制的最高位后面的所有位都变成1,然后再加1,得到比cap大且最接近的2的n次幂的数。

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);
}
static final int tableSizeFor(int cap) {
    // 0001 0100 20
    // 0001 0011 19
    int n = cap - 1;
    // 0000 1001 9
    // 0001 1011 27
    n |= n >>> 1;
    // 0000 0110 6
  	// 0001 1111 31
    n |= n >>> 2;
    // 0000 0001
    // 0001 1111 31
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

HashMap(int initialCapacity)

指定map的初始容量。里面是直接调用的上面的方法。

HashMap()

使用默认的负载系数创建map对象。

public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
}

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

使用给定map构造一个map对象。

使用默认的负载系数。使用给定map的size计算出threshold,知道这两个值,就跟HashMap(int initialCapacity, float loadFactor)类似了。最后就是调用putVal了。

public HashMap(Map<? extends K, ? extends V> m) {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
}
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
    int s = m.size();
    if (s > 0) {
        // 这个分支里面的操作是为了计算出threshold,然后for循环调用putVal。
        if (table == null) { // pre-size
            // 计算出initialCapacity,+1.0F是位了解决float的小数,确保size足够。
            float ft = ((float)s / loadFactor) + 1.0F;
            int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                     (int)ft : MAXIMUM_CAPACITY);
            if (t > threshold)
                // 计算出上面比t大且离t最近的2的n次幂的阀值。
                threshold = tableSizeFor(t);
        }
        else if (s > threshold)
            resize();
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
            K key = e.getKey();
            V value = e.getValue();
            putVal(hash(key), key, value, false, evict);
        }
    }
}

方法

int size()

获取当前map中Node的数量。

boolean isEmpty()

判断当前map中Node的数量是否为0。

V get(Object key)

根据给定的key找到对应Node,并返回Node中的value。

先用给定的key调用hash方法获取其哈希码,然后调用getNode获取key对应的Node

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}
// 计算给定key的哈希码
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
// 根据给定的哈希码和key查询Node
final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
  	// 判断key在table中的位置,并判断该位置上的第一个Node是否不为null。
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        // 检查第一个Node的哈希码和给定哈希码是否相同,并且key是否相同。如果都相同就直接返回该Node。
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        // 如果第一个Node不是的,就去找该Node的下一个。
        if ((e = first.next) != null) {
            // 如果第一个Node是TreeNode,就说明当前存储方式是红黑树,就用getTreeNode去查找。
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            // 如果当前是链表存储,就去遍历找一下Node判断他的哈希码和key。
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}

boolean containsKey(Object key)

判断map中是否包含给定的key。

直接调用的getNode判断是否不为null。

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

V put(K key, V value)

将给定的key和value保存到map中。

put涉及到了一些操作

  • 给table扩容,分首次put初始化和给定容量初始化,还有超过阀值扩容
  • 将链表中的元素重写计算位置之后,放到新的table中。因为扩容之后的容量是原来的2倍,所有(e.hash & oldCap)计算出来的结果如果等于0说明位置在oldCap范围内,如果计算结果大于0说明位置咋爱oldCap外,(那么外是什么位置呢?),由于特殊的扩容方式,这里的外就是在原来的位置上加上一个oldCap的距离。
  • 链表超过阀值,转为红黑树
  • 如果key与存在的key冲突,直接使用当前value替换就的value,并返回旧value。

两组测试数据:

0010 0000 假设oldCap=32
0001 0100 第一个hash=20 0000 0000 结果为0,在oldtab中的位置是(32-1)&20=20,在newTab中(64-1)&20=20
0010 0001 第二个hash=33 0010 0000 结果为32,在oldtab中的位置是(32-1)&33=1,在newTab中(64-1)&33=33,33=32+1

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}
// put键值对的方法
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
  	// 首先判断table是否为空,是就去扩容。
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
  	// 判断table中的i位置上是否有元素,(n - 1) & hash是计算当前key落在table哪个位置,
    if ((p = tab[i = (n - 1) & hash]) == null)
      	// 创建一个Node对象,并赋值给table的指定位置。
        tab[i] = newNode(hash, key, value, null);
    else {
      	// 如果说计算出的table所在位置i上面有元素
        Node<K,V> e; K k;
      	//如果当前要插入的key与table[i]位置上的Node的key相同,就把原来的Node p赋值给e,后面会进行判断使用插入的value替换e的value
        if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
      	// 如果p是TreedNode,就调用TreeNode的putTreeVal方法
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
      	// 如果当前插入的键值对与table[i]位置上的Node的key不相同,并且当前元素也不是TreeNode,就去操作table[i]位置的链表
        else {
          	// for循环遍历,记录下链表的深度
            for (int binCount = 0; ; ++binCount) {
              	// 找到链表的尾部,将最后一个元素的next指向我们插入的新Node
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                  	// 如果链表的插入大于等于了8,就转成红黑树
                    if (binCount >= TREEIFY_THRESHOLD - 1) // 
                        treeifyBin(tab, hash);
                    break;
                }
              	// 如果在遍历的过程中,找到了与待插入key相同的key,就直接跳出循环,去指向外面的替换value的操作
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
      	// e表示我们找到的与待插入键值对key相同的Node
      	// 如果e不为null
        if (e != null) { // existing mapping for key
          	// oldValue存起来,一会儿返回的
            V oldValue = e.value;
          	// 如果onlyIfAbsent是false或者oldValue是null,就去替换value。
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
          	// 这个是空方法
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
  	// 如果是插入了一个新的Node,而不是替换的value,
  	// 就去把size+1,然后计算size是否超过了阀值threshold,如果超过了去扩容
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}
// 扩容方法
final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
  	// 如果当前table的长度大于0
    if (oldCap > 0) {
      	// 如果大于等于了最大容量,就设置扩容阀值为int的最大值。
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
      	// 如果当前table的容量乘以2小于最大容量,并且大于初始容量,就将容量扩容为原来的两倍
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    }
  	// 这种一般是初始化的时候指定了容量,比如调用new HashMap(20)
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
  	// 一般第一次调用put的时候,会使用默认容量对table进行初始化
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
  	// 如果newThr是0,就使用newCap去计算newCap
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY 
                  (int)ft : Integer.MAX_VALUE);
    }
  	// 将新的扩容阀值赋值给threshold
    threshold = newThr;
    @SuppressWarnings({"rawtypes","unchecked"})
  	// 使用新的容量去创建Node数组
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab;
    if (oldTab != null) {
      	// 对旧的table中的元素重写计算索引位置,放到新的newTab中。
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
          	// 如果oldTab[j]位置上的元素不为null
            if ((e = oldTab[j]) != null) {
              	// 就将oldTab[j]设置为null
                oldTab[j] = null;
              	// 判断他是否有next节点。没有就直接将该元素放到newTab中。
                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
                  	// 存放低位(0~oldCap)的Node
                    Node<K,V> loHead = null, loTail = null;
                  	// 存放高位(oldCap~oldCap+j)
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                      	// 如果hash落在了0~oldCap内
// 0010 0000 假设oldCap=32
// 0001 0100 第一个hash=20 0000 0000 结果为0,在oldtab中的位置是(32-1)&20=20,在newTab中(64-1)&20=20
// 0010 0001 第二个hash=33 0010 0000 结果为32,在oldtab中的位置是(32-1)&33=1,在newTab中(64-1)&33=33
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                      	// 如果hash落在了oldCap~oldCap+j内
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                  	// 低位存放到newTab中
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                  	// 高位存放到newTab中
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

putAll(Map<? extends K, ? extends V> m)

将给定map中的元素添加到当前map中。

putMapEntries方法上面已经分析过了。

public void putAll(Map<? extends K, ? extends V> m) {
    putMapEntries(m, true);
}

V remove(Object key)

根据key删除对应的Node,返回被删除的Node。

public V remove(Object key) {
    Node<K,V> e;
  	// key表示要删除的Node的key
  	// null表示Node的value
  	// false表示不比较value
  	// true表示可以移动其他Node
    return (e = removeNode(hash(key), key, null, false, true)) == null ?
        null : e.value;
}
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;
  	// 判断给定的key在map中是否存在
    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;
      	// 判断index位置上的第一个元素是不是我们要删除的元素,是的就将该元素赋值给node
        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);
            }
        }
      	// 下面的条件分支与上面的是对应的
      	// 如果我们找到了要删除的node,就matchValue=false,就不去比较value,
        if (node != null && (!matchValue || (v = node.value) == value ||
                             (value != null && value.equals(v)))) {
          	// 如果该节点是TreeNode就去调用removeTreeNode方法删除
            if (node instanceof TreeNode)
                ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
          	// 如果index位置上的第一个节点就是我们要删除的节点,就直接将该节点的下一个节点放在index位置上
            else if (node == p)
                tab[index] = node.next;
          	// 如果是在链表上找到的(非头节点)
          	// p表示要删除节点的上一个节点,next表示要删除的节点
            else
              	// 将要删除节点从链表中剔除
                p.next = node.next;
            ++modCount;
            --size;
            afterNodeRemoval(node);
          	// 返回被删除的节点
            return node;
        }
    }
    return null;
}

void clear()

情况map中的所有Node。

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;
    }
}

boolean containsValue(Object value)

判断map中是否包含给定的key

public boolean containsValue(Object value) {
    Node<K,V>[] tab; V v;
    if ((tab = table) != null && size > 0) {
      	// 遍历table
        for (int i = 0; i < tab.length; ++i) {
          	// 遍历链表
            for (Node<K,V> e = tab[i]; e != null; e = e.next) {
                if ((v = e.value) == value ||
                    (value != null && value.equals(v)))
                    return true;
            }
        }
    }
    return false;
}

Set<K> keySet()

返回key的集合

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

Collection<V> values()

返回map中value的集合

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

Set<Map.Entry<K,V>> entrySet()

返回map中的Node集合

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

V getOrDefault(Object key, V defaultValue)

根据key获取对应的value,如果key不存在,就返回defaultValue

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

V putIfAbsent(K key, V value)

如果给定的key不存在就是将key-value保存到map中,如果给定的key存在,并且value是null,将使用给定的value替换,然后返回null。如果旧的value不为null,就不做任何修改直接返回旧的value。

public V putIfAbsent(K key, V value) {
  	// 第一个true表示不修改已经存在的值
		// 第二个true表示table不是创建模式
    return putVal(hash(key), key, value, true, true);
}

boolean remove(Object key, Object value)

删除给定的键值对。

public boolean remove(Object key, Object value) {
  	// 第一个ture表示是否vaule匹配才删除
    return removeNode(hash(key), key, value, true, true) != null;
}

boolean replace(K key, V oldValue, V newValue)

将指定key-value的value使用给定的新value替换,替换成功返回true

public boolean replace(K key, V oldValue, V newValue) {
    Node<K,V> e; V v;
    if ((e = getNode(hash(key), key)) != null &&
        ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
        e.value = newValue;
        afterNodeAccess(e);
        return true;
    }
    return false;
}

V replace(K key, V value)

使用给定的value替换给定key的value。返回旧的value的值。

public V replace(K key, V value) {
    Node<K,V> e;
    if ((e = getNode(hash(key), key)) != null) {
        V oldValue = e.value;
        e.value = value;
        afterNodeAccess(e);
        return oldValue;
    }
    return null;
}

V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)

如果给定key的value为null,就使用mappingFunction计算出一个value,如果计算出的value不为null就将该value设置为指定key的value。

public V computeIfAbsent(K key,
                         Function<? super K, ? extends V> mappingFunction) {
    if (mappingFunction == null)
        throw new NullPointerException();
    int hash = hash(key);
    Node<K,V>[] tab; Node<K,V> first; int n, i;
    int binCount = 0;
    TreeNode<K,V> t = null;
    Node<K,V> old = null;
  	// 看要不要去扩容
    if (size > threshold || (tab = table) == null ||
        (n = tab.length) == 0)
        n = (tab = resize()).length;
  	// 找到给定key对应的Node
    if ((first = tab[i = (n - 1) & hash]) != null) {
        if (first instanceof TreeNode)
            old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
        else {
            Node<K,V> e = first; K k;
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                    old = e;
                    break;
                }
                ++binCount;
            } while ((e = e.next) != null);
        }
        V oldValue;
        if (old != null && (oldValue = old.value) != null) {
            afterNodeAccess(old);
            return oldValue;
        }
    }
  	// 计算出新的value
    V v = mappingFunction.apply(key);
    if (v == null) {
        return null;
    } else if (old != null) {
      	// 如果给定的key存在,并且计算出的value不为null,就是使用计算出的value替换就的value
        old.value = v;
        afterNodeAccess(old);
        return v;
    }
  	// 如果是红黑树
    else if (t != null)
      	// 调用红黑树的putTreeVal替换value
        t.putTreeVal(this, tab, hash, key, v);
    else {
      	// 如果map中不存给定key的键值对,就创建Node,存入
        tab[i] = newNode(hash, key, v, first);
      	// 计算当前链表是否需要转为红黑树
        if (binCount >= TREEIFY_THRESHOLD - 1)
            treeifyBin(tab, hash);
    }
    ++modCount;
    ++size;
    afterNodeInsertion(true);
    return v;
}

V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)

如果给定key的value不为null,就使用remappingFunction计算出一个value,如果该value不为null,就设置给指定的key并返回。如果给定key的value为null,就删除该key-value。

public V computeIfPresent(K key,
                          BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
    if (remappingFunction == null)
        throw new NullPointerException();
    Node<K,V> e; V oldValue;
    int hash = hash(key);
    if ((e = getNode(hash, key)) != null &&
        (oldValue = e.value) != null) {
        V v = remappingFunction.apply(key, oldValue);
        if (v != null) {
            e.value = v;
            afterNodeAccess(e);
            return v;
        }
        else
            removeNode(hash, key, null, false, true);
    }
    return null;
}

V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)

如果remappingFunction计算处的新value不为null,就将key-value保存到map中。如果新value为null,并且给定key是存在的,就删除给定key对应键值对。

public V compute(K key,
                 BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
    if (remappingFunction == null)
        throw new NullPointerException();
    int hash = hash(key);
    Node<K,V>[] tab; Node<K,V> first; int n, i;
    int binCount = 0;
    TreeNode<K,V> t = null;
    Node<K,V> old = null;
    if (size > threshold || (tab = table) == null ||
        (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((first = tab[i = (n - 1) & hash]) != null) {
        if (first instanceof TreeNode)
            old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
        else {
            Node<K,V> e = first; K k;
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                    old = e;
                    break;
                }
                ++binCount;
            } while ((e = e.next) != null);
        }
    }
    V oldValue = (old == null) ? null : old.value;
    V v = remappingFunction.apply(key, oldValue);
    if (old != null) {
        if (v != null) {
            old.value = v;
            afterNodeAccess(old);
        }
        else
            removeNode(hash, key, null, false, true);
    }
    else if (v != null) {
        if (t != null)
            t.putTreeVal(this, tab, hash, key, v);
        else {
            tab[i] = newNode(hash, key, v, first);
            if (binCount >= TREEIFY_THRESHOLD - 1)
                treeifyBin(tab, hash);
        }
        ++modCount;
        ++size;
        afterNodeInsertion(true);
    }
    return v;
}

V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)

如果给定的key存在,并且value不为null,就用remappingFunction计算出来的结果替换value(如果计算出的value为null就删除该节点),如果value为null,就直接使用给定的value替换旧的value。如果给定key不存在,就将给定的key-value保存到map中。

public V merge(K key, V value,
               BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
    if (value == null)
        throw new NullPointerException();
    if (remappingFunction == null)
        throw new NullPointerException();
    int hash = hash(key);
    Node<K,V>[] tab; Node<K,V> first; int n, i;
    int binCount = 0;
    TreeNode<K,V> t = null;
    Node<K,V> old = null;
  	// 扩容
    if (size > threshold || (tab = table) == null ||
        (n = tab.length) == 0)
        n = (tab = resize()).length;
  	// 找Node
    if ((first = tab[i = (n - 1) & hash]) != null) {
        if (first instanceof TreeNode)
            old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
        else {
            Node<K,V> e = first; K k;
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                    old = e;
                    break;
                }
                ++binCount;
            } while ((e = e.next) != null);
        }
    }
  	// 找到的Node不为null
    if (old != null) {
        V v;
      	// 如果Node的value不为null,使用给定的value和key对应的value计算出一个新的value
        if (old.value != null)
            v = remappingFunction.apply(old.value, value);
        else
          	// 如果Node的value是null,直接将给定value作为新value
            v = value;
      	// 新value不为null,就直接替换Node的value
        if (v != null) {
            old.value = v;
            afterNodeAccess(old);
        }
        else
          	// 如果计算出的新的value为null,就删除该Node
            removeNode(hash, key, null, false, true);
        return v;
    }
  	// 如果找不到给定key的Node
    if (value != null) {
      	// 如果是红黑树,就调用putTreeVal创建新的节点
        if (t != null)
            t.putTreeVal(this, tab, hash, key, value);
        else {
          	// 如果是链表,就直接newNode
            tab[i] = newNode(hash, key, value, first);
          	// 判断链表是否需要转为红黑树
            if (binCount >= TREEIFY_THRESHOLD - 1)
                treeifyBin(tab, hash);
        }
        ++modCount;
        ++size;
        afterNodeInsertion(true);
    }
    return value;

forEach(BiConsumer<? super K, ? super V> action)

遍历map中的所有key-value对。

public void forEach(BiConsumer<? super K, ? super V> action) {
    Node<K,V>[] tab;
    if (action == null)
        throw new NullPointerException();
    if (size > 0 && (tab = table) != null) {
        int mc = modCount;
        for (int i = 0; i < tab.length; ++i) {
            for (Node<K,V> e = tab[i]; e != null; e = e.next)
                action.accept(e.key, e.value);
        }
        if (modCount != mc)
            throw new ConcurrentModificationException();
    }
}

void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)

遍历所有Node,使用function计算出新的value,替换旧的value

public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
    Node<K,V>[] tab;
    if (function == null)
        throw new NullPointerException();
    if (size > 0 && (tab = table) != null) {
        int mc = modCount;
        for (int i = 0; i < tab.length; ++i) {
            for (Node<K,V> e = tab[i]; e != null; e = e.next) {
                e.value = function.apply(e.key, e.value);
            }
        }
        if (modCount != mc)
            throw new ConcurrentModificationException();
    }
}

总结

HashMap默认使用无参构造器去创建事,底层table数组默认是null,只有在第一次put数据的时候才会去初始化table,默认初始化容量为16,扩容阀值是12,最大容量是2的30次方。默认负载系数的0.75,扩容阀值=负载系数*容量

如果链表的长度大于8,并且table.length>=64,就讲链表转为红黑树,如果红黑树的节点<=6就讲红黑树退化成链表。

作为key的对象,必须重写equalshashCode方法。

posted @ 2020-09-20 18:45  Godfunc  阅读(238)  评论(0编辑  收藏  举报