HashMap 源码分析07之compute()、computeIfAbsent()、computeIfPresent()

1. compute

  • 指定 key,找到对应的Node节点,执行remappingFunction.apply(key,value),返回新的 value 值,会出现四种情况
    • Node 为 null,value为 null :不用进行操作,返回 value
    • Node 不为 null,value为 null :删除 Node 节点,返回 value
    • Node 为 null,value 不为 null :新建节点(key, value),返回 value
    • Node 不为 null,value 不为 null :更新Node节点的值,返回 value
@Override
public V compute(K key,
    BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
    /* null 判断 */             
    if (remappingFunction == null)
        throw new NullPointerException();
    /* 计算 key 的 hash 值 */    
    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;
    /* 元素个数 > 临界值 || tab 为空 || tab 长度为 0 */
    if (size > threshold || (tab = table) == null ||
        (n = tab.length) == 0)
        /* 进行扩容并获得 扩容后的数组长度 */
        n = (tab = resize()).length;
    /* 拿到 key 值对应的桶位下标,并获取首元素的节点 */    
    if ((first = tab[i = (n - 1) & hash]) != null) {
    	/* first 若属于树节点,使用树节点的查找Node节点方法getTreeNode() */
        if (first instanceof TreeNode)
            old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
        else {
        	/* 链表查找 node 节点 */
            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);
        }
    }
    /* 获取旧结点的值,不存在返回 null */
    V oldValue = (old == null) ? null : old.value;
    /* 调用指定的执行方法,传入 key,oldValue */
    V v = remappingFunction.apply(key, oldValue);
    /* 旧结点不为 null */
    if (old != null) {
    	/* 指定方法执行后,若返回 null,删除该节点,否则 old节点的值设置为返回的值 */
        if (v != null) {
            old.value = v;
            /* 预留方法 */
            afterNodeAccess(old);
        }
        else
            removeNode(hash, key, null, false, true);
    }
    /* 旧节点为null,remappingFunction.apply() 返回的 v 不为null */
    else if (v != null) {
        /*  != null 则当前桶位的节点为 树节点 */
        if (t != null)
        	/* 调用树节点的添加方法,value 为返回的 v 值 */
            t.putTreeVal(this, tab, hash, key, v);
        else {
        	/* 当前桶位的节点为链节点,添加一个新元素(key,v) */
            tab[i] = newNode(hash, key, v, first);
            /* binCount >= 7 即元素有 8 个时,尝试转化成红黑树  */
            if (binCount >= TREEIFY_THRESHOLD - 1)
                treeifyBin(tab, hash);
        }
        /* 更新 modCount, size */
        ++modCount;
        ++size;
        /* 预留函数 */
        afterNodeInsertion(true);
    }
    /* 返回 v 值 即新值 */
    return v;
}

2. computeIfAbsent

  • 指定 key,找到对应的Node节点,执行remappingFunction.apply(key),返回新的 value 值,会执行以下四种情况:
    • Node 不为 null 且 Node.value 不为null,返回 Node.value
    • value 为 null,直接返回 null
    • value 不为null,Node不为null 且 Node.value为null,设置 Node.value为value,返回 value
    • value 不为null,Node为null,新建节点 (key, v), 返回 value
@Override
public V computeIfAbsent(K key,
                         Function<? super K, ? extends V> mappingFunction) {
    /* null 异常检测 */
    if (mappingFunction == null)
        throw new NullPointerException();
    /* 计算 key 的 hash 值 */    
    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;
    /* 元素个数 > 临界值 || tab 为空 || tab 长度为 0 */
    if (size > threshold || (tab = table) == null ||
        (n = tab.length) == 0)
        /* 进行扩容并获得 扩容后的数组长度 */
        n = (tab = resize()).length;
    /* 拿到 key 值对应的桶位下标,并获取首元素的节点 */
    if ((first = tab[i = (n - 1) & hash]) != null) {
    	/* first 若属于树节点,使用树节点的查找Node节点方法getTreeNode() */
        if (first instanceof TreeNode)
            old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
        else {
        	/* 链表查找 node 节点 */
            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);
        }
        /* old 不为 null && old.value 不为 null,返回旧值oldValue */
        V oldValue;
        if (old != null && (oldValue = old.value) != null) {
        	/* 预留函数 */
            afterNodeAccess(old);
            return oldValue;
        }
    }
    /* 执行指定的操作 */
    V v = mappingFunction.apply(key);
    /* v 为 null 直接 return null */
    if (v == null) {
        return null;
    } else if (old != null) { // old 不为null,但old.value 为 null
        /* 设置旧值为 v */
        old.value = v;
        /* 预留函数 */
        afterNodeAccess(old);
        return v;
    }
    /* t != null 即 当前桶位的元素为树节点,添加新值(key, v) */
    else if (t != null)
        t.putTreeVal(this, tab, hash, key, v);
    else {
    	/* 添加链节点(key,v) */
        tab[i] = newNode(hash, key, v, first);
        /* 判断 binCount >= 8 - 1,是否需要转化树节点 */
        if (binCount >= TREEIFY_THRESHOLD - 1)
            treeifyBin(tab, hash);
    }
    /* 更新 modCount,size */
    ++modCount;
    ++size;
    /* 预留函数 */
    afterNodeInsertion(true);
    return v;
}

3. computeIfPresent

  • 找不到 key 对应的 Node 节点 || Node.value 为 null,直接返回 null
  • 执行指定动作返回 value,若 value 为 null ,删除 key 对应的 Node 节点返回null,否则,替换旧值为value并返回 value
public V computeIfPresent(K key,
      BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
    /* null 异常检测 */                      	
    if (remappingFunction == null)
        throw new NullPointerException();
    Node<K,V> e; V oldValue;
    /* 计算 key 的 hash 值 */
    int hash = hash(key);
    /* e 不为空 && e.value 不为空 */
    if ((e = getNode(hash, key)) != null &&
        (oldValue = e.value) != null) {
        /* 执行指定动作 返回v */
        V v = remappingFunction.apply(key, oldValue);
        /* v != null 则替换该 value 值 */
        if (v != null) {
            e.value = v;
            /* 预留函数 */
            afterNodeAccess(e);
            return v;
        }
        else
        	/* v 为null 则删除 key 对应的节点 */
            removeNode(hash, key, null, false, true);
    }
    /* 找不到 key 对应的节点 或者 Node.value 为空,直接返回 null */
    return null;
}

4. 代码小结

4.1 compute()、computeIfAbsent()、computeIfPresent() 的使用

import java.util.HashMap;

public class HashMapTest06 {
    public static void main(String[] args) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        for(int i = 1; i <= 8; i++) {
            hashMap.put(i, i);
        }
        System.out.println("now hashmap: " + hashMap);
        System.out.println("=============== compute ============");
        /* 注意: value = remappingFunction.apply(key,value)
        *  1. Node 为 null,value为 null :不用进行操作,返回 value
        *  2. Node 不为 null,value为 null :删除 Node 节点,返回 value
        *  3. Node 为 null,value 不为 null :新建节点(key, value),返回 value
        *  4. Node 不为 null,value 不为 null :更新Node节点的值,返回 value
        * */
        System.out.println("hashMap.compute(10, (k, v) -> null): " + hashMap.compute(10, (k, v) -> null));
        System.out.println("now hashmap: " + hashMap);
        System.out.println("hashMap.compute(1, (k, v) -> null): " + hashMap.compute(1, (k, v) -> null));
        System.out.println("now hashmap: " + hashMap);
        System.out.println("hashMap.compute(10, (k, v) -> 10): " + hashMap.compute(10, (k, v) -> 10));
        System.out.println("now hashmap: " + hashMap);
        System.out.println("hashMap.compute(2, (k, v) -> 20): " + hashMap.compute(2, (k, v) -> 20));
        System.out.println("now hashmap: " + hashMap);

        System.out.println("=============== computeIfAbsent ============");
        /* 注意: value = remappingFunction.apply(key)
        * 1. Node 不为 null 且 Node.value 不为null,返回 Node.value
	    * 2. Node 不为 null 且 Node.value 为 null,value 为 null,直接返回 null
        * 3. value 不为null,Node不为null 且 Node.value为null,设置 Node.value为value,返回 value
	    * 4. value 不为null,Node为null,新建节点 (key, v), 返回 value
        */
        hashMap.put(50, null);
        System.out.println("hashMap.put(50, null), now hashmap: " + hashMap);
        System.out.println("hashMap.computeIfAbsent(3, (k) -> 30): " + hashMap.computeIfAbsent(3, (k) -> 30));
        System.out.println("now hashmap: " + hashMap);
        System.out.println("hashMap.computeIfAbsent(50, (k) -> null): " + hashMap.computeIfAbsent(50, (k) -> null));
        System.out.println("now hashmap: " + hashMap);
        System.out.println("hashMap.computeIfAbsent(50, (k) -> 50): " + hashMap.computeIfAbsent(50, (k) -> 50));
        System.out.println("now hashmap: " + hashMap);
        System.out.println("hashMap.computeIfAbsent(9, (k) -> 9): " + hashMap.computeIfAbsent(9, (k) -> 9));
        System.out.println("now hashmap: " + hashMap);

        System.out.println("=============== computeIfPresent ============");
        /* 注意: value = remappingFunction.apply(key,value)
        * 1. Node 为 null || Node.value 为 null,直接返回 null
        * 2. Node 不为 null,Node.value 不为null, value 为null,删除 Node
        * 3. Node 不为 null,Node.value 不为null, value 不为null,更新 Node.value 为 value
        * */
        hashMap.put(100, null);
        System.out.println("now hashmap: " + hashMap);
        System.out.println("hashMap.computeIfPresent(20, (k, v) -> 20): " + hashMap.computeIfPresent(20, (k, v) -> 20));
        System.out.println("now hashmap: " + hashMap);
        System.out.println("hashMap.computeIfPresent(100, (k, v) -> 100): " + hashMap.computeIfPresent(100, (k, v) -> 100));
        System.out.println("now hashmap: " + hashMap);
        System.out.println("hashMap.computeIfPresent(6, (k, v) -> 60): " + hashMap.computeIfPresent(6, (k, v) -> 60));
        System.out.println("now hashmap: " + hashMap);
        System.out.println("hashMap.computeIfPresent(7, (k, v) -> null): " + hashMap.computeIfPresent(7, (k, v) -> null));
        System.out.println("now hashmap: " + hashMap);
    }
}

  • 输出如下:
now hashmap: {1=1, 2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8}
=============== compute ============
hashMap.compute(10, (k, v) -> null): null
now hashmap: {1=1, 2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8}
hashMap.compute(1, (k, v) -> null): null
now hashmap: {2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8}
hashMap.compute(10, (k, v) -> 10): 10
now hashmap: {2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 10=10}
hashMap.compute(2, (k, v) -> 20): 20
now hashmap: {2=20, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 10=10}
=============== computeIfAbsent ============
hashMap.put(50, null), now hashmap: {2=20, 50=null, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 10=10}
hashMap.computeIfAbsent(3, (k) -> 30): 3
now hashmap: {2=20, 50=null, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 10=10}
hashMap.computeIfAbsent(50, (k) -> null): null
now hashmap: {2=20, 50=null, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 10=10}
hashMap.computeIfAbsent(50, (k) -> 50): 50
now hashmap: {2=20, 50=50, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 10=10}
hashMap.computeIfAbsent(9, (k) -> 9): 9
now hashmap: {2=20, 50=50, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 9=9, 10=10}
=============== computeIfPresent ============
now hashmap: {2=20, 50=50, 3=3, 4=4, 100=null, 5=5, 6=6, 7=7, 8=8, 9=9, 10=10}
hashMap.computeIfPresent(20, (k, v) -> 20): null
now hashmap: {2=20, 50=50, 3=3, 4=4, 100=null, 5=5, 6=6, 7=7, 8=8, 9=9, 10=10}
hashMap.computeIfPresent(100, (k, v) -> 100): null
now hashmap: {2=20, 50=50, 3=3, 4=4, 100=null, 5=5, 6=6, 7=7, 8=8, 9=9, 10=10}
hashMap.computeIfPresent(6, (k, v) -> 60): 60
now hashmap: {2=20, 50=50, 3=3, 4=4, 100=null, 5=5, 6=60, 7=7, 8=8, 9=9, 10=10}
hashMap.computeIfPresent(7, (k, v) -> null): null
now hashmap: {2=20, 50=50, 3=3, 4=4, 100=null, 5=5, 6=60, 8=8, 9=9, 10=10}

Process finished with exit code 0

4.2 使用 compute(),链节点转化为红黑树的元素 >= 8,这要与 put >= 9 区分

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class HashMapTest05 {
    private static String[] base = new String[] {"Aa", "BB"};
    /* 生成 2^n 个值 */
    public static List<String> generateN(int n) {
        if(n <= 0) return null;

        List<String> list = generateOne(null);
        for(int i = 1; i < n; ++i) {
            list = generateOne(list);
        }

        return list;
    }
    /* 生成 2 个 hashcode 相同的值 */
    public static List<String> generateOne() {
        return generateOne(null);
    }

    public static List<String> generateOne(List<String> strList) {
        if((null == strList) || (0 == strList.size())) {
            strList = new ArrayList<String>();
            for(int i = 0; i < base.length; ++i) {
                strList.add(base[i]);
            }

            return strList;
        }

        List<String> result = new ArrayList<String>();

        for(int i = 0; i < base.length; ++i) {
            for(String str: strList) {
                result.add(base[i]  + str);
            }
        }

        return result;
    }
    public static void main(String[] args) throws Exception {
        HashMap<String, Integer> hashMap = new HashMap<>();
        /* 反射获取对象 */
        Field table = hashMap.getClass().getDeclaredField("table");
        Field threshold = hashMap.getClass().getDeclaredField("threshold");
        /* 不设置不能获取私有属性 */
        table.setAccessible(true);
        threshold.setAccessible(true);
        /* 设置 16 个 hashCode 相同的值 */
        List<String> list = generateN(4);
        for(int i = 0; i < list.size(); i++) {
            hashMap.compute(list.get(i), (key, value) -> 10);

            Object[] objects = (Object[]) table.get(hashMap);
            System.out.println("now objects 的长度为:" + objects.length);
            System.out.println("now hashMap 有" + (i + 1) + "个元素:" + "\t");
            for (Object object : objects) {
                if(object == null)
                    System.out.print(object + "\t");
                else
                    System.out.print(object.getClass() + "\t");
            }
            System.out.println();
            System.out.println("=================");
        }
    }
}
  • 输出如下:这里可以回看 03 篇,区别在于第 8 个元素的时候,compute() 进行了一次尝试树化(没成功,扩容),而 put() 在第 9 个元素的时候才尝试进行树化操作。
  • 原因解释:在链条遍历时,compute() 是 first 开始遍历,bitCount ++,而 put() 是 first.next 开始遍历,bitCount ++,这就是相差为 1 的原因。
now objects 的长度为:16
now hashMap 有1个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$Node	null	null	null	null	null	null	
=================
now objects 的长度为:16
now hashMap 有2个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$Node	null	null	null	null	null	null	
=================
now objects 的长度为:16
now hashMap 有3个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$Node	null	null	null	null	null	null	
=================
now objects 的长度为:16
now hashMap 有4个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$Node	null	null	null	null	null	null	
=================
now objects 的长度为:16
now hashMap 有5个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$Node	null	null	null	null	null	null	
=================
now objects 的长度为:16
now hashMap 有6个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$Node	null	null	null	null	null	null	
=================
now objects 的长度为:16
now hashMap 有7个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$Node	null	null	null	null	null	null	
=================
now objects 的长度为:32
now hashMap 有8个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$Node	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	
=================
now objects 的长度为:64
now hashMap 有9个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$Node	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	
=================
now objects 的长度为:64
now hashMap 有10个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$TreeNode	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	
=================
now objects 的长度为:64
now hashMap 有11个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$TreeNode	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	
=================
now objects 的长度为:64
now hashMap 有12个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$TreeNode	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	
=================
now objects 的长度为:64
now hashMap 有13个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$TreeNode	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	
=================
now objects 的长度为:64
now hashMap 有14个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$TreeNode	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	
=================
now objects 的长度为:64
now hashMap 有15个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$TreeNode	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	
=================
now objects 的长度为:64
now hashMap 有16个元素:	
null	null	null	null	null	null	null	null	null	class java.util.HashMap$TreeNode	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	null	
=================

Process finished with exit code 0

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值