zoukankan      html  css  js  c++  java
  • 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方法。

  • 相关阅读:
    oracle中的游标
    Oracle中的表空间
    Oracle中建表及表操作
    oracle中的权限管理
    oracle中的数据类型
    Oracle中常用的系统函数
    oracle中的dual表简介
    Oracle中常用的系统表
    Git常用命令总结
    Git配置文件与git config命令
  • 原文地址:https://www.cnblogs.com/Godfunc/p/13701398.html
Copyright © 2011-2022 走看看