跳至主要內容

JDK8中的compute、computeIfAbsent、computeIfPresent

cylin...大约 3 分钟

写在前面

三个方法的区别:

  • compute:计算并更新值
  • computeIfAbsentValue不存在时才计算
  • computeIfPresentValue存在时才计算

computeIfAbsent不存在时,才会更新,所以只需要传入一个Key即可,而其他的两个都需要传递一个map键值对

compute

使用

// map中如果存在 key 则将 value 值+1, 如果不存在则 置为 1
map.compute(cur_sum, (key, value) -> value != null ? value+1 : 1);

源码

default V compute(K key,
        BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        
    // 函数式接口不能为空    
    Objects.requireNonNull(remappingFunction);
    
    // 获取旧值
    V oldValue = get(key);

    // 获取计算的新值
    V newValue = remappingFunction.apply(key, oldValue);
    
    if (newValue == null) { // 新值为空
        // delete mapping
        if (oldValue != null || containsKey(key)) { // 旧值存在时
            // 移除该键值
            remove(key);
            return null;
        } else {
            // nothing to do. Leave things as they were.
            return null;
        }
    } else { // 新值不为空
        // 添加或者覆盖旧值
        put(key, newValue);
        return newValue;
    }
}

computeIfAbsent

使用

// Function<? super K, ? extends V> K是传入类型,V是返回类型
Map<Integer, Integer> map = new HashMap<>();
map.put(1, 11);
map.put(2, 22);

// 传入类型 -> 返回类型
// 如果用不到传入类型,可以直接省略掉
// map.computeIfAbsent(2, 999)
map.computeIfAbsent(2, key -> key+999);
map.computeIfAbsent(3, key -> key+999);
System.out.println(map.get(2));
System.out.println(map.get(3));

源码

    /**
     * 如果指定的key不存在于映射中,则根据指定的映射函数进行计算并将其放入映射中。
     * 如果映射函数返回null,则不执行任何操作,直接返回null。
     *
     * @param key             要计算新值的key
     * @param mappingFunction 根据指定的key计算新值的映射函数
     * @return 计算出的新值,如果key已存在于映射中,则返回其对应的旧值;如果映射函数返回null,则返回null
     * @throws NullPointerException 如果指定的映射函数为null
     */
    default V computeIfAbsent(K key,
                              Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);
        V v;
        // 尝试获取key对应的值
        if ((v = get(key)) == null) {
            V newValue;
            // 根据映射函数计算新值
            if ((newValue = mappingFunction.apply(key)) != null) {
                // 将新值放入映射中
                put(key, newValue);
                return newValue;
            }
        }
        // 如果key已存在于映射中,则返回其对应的旧值;如果映射函数返回null,则返回null
        return v;
    }

computeIfPresent

使用

map.computeIfPresent(3, (key, value)-> value+1);

源码

/**
 * 如果指定的键存在且其关联的值不为 null,则尝试根据提供的 remappingFunction 对其进行重新映射。
 * 如果 remappingFunction 返回非 null 的值,则将其与键关联;如果返回 null,则移除键的映射关系。
 * 如果指定的键不存在或其关联的值为 null,则不执行任何操作并返回 null。
 *
 * @param key               要重新映射的键
 * @param remappingFunction 用于重新映射的函数,接受键和其当前关联值作为参数,并返回新的值
 * @return 如果指定的键存在且已成功重新映射,则返回新的映射值;如果键不存在或重新映射为 null,则返回 null
 * @throws NullPointerException 如果 remappingFunction 为 null
 */
default V computeIfPresent(K key,
        BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
    Objects.requireNonNull(remappingFunction);
    V oldValue;
    if ((oldValue = get(key)) != null) {
        // 通过 remappingFunction 获取新值
        V newValue = remappingFunction.apply(key, oldValue);
        if (newValue != null) {
            // 如果新值不为 null,则用新值替换旧值
            put(key, newValue);
            return newValue;
        } else {
            // 如果新值为 null,则移除键的映射关系
            remove(key);
            return null;
        }
    } else {
        // 如果指定的键不存在或其关联的值为 null,则不执行任何操作并返回 null
        return null;
    }
}