HashMap中的compute、computeIfAbsent、computeIfPresent

本文详细解析了HashMap中的compute、computeIfAbsent及computeIfPresent方法的工作原理与应用场景,通过具体示例展示了如何利用这些方法高效地更新或获取Map中的元素。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HashMap中的compute、computeIfAbsent、computeIfPresent

1、compute

在这里插入图片描述

1)查看compute方法的文档:

在这里插入图片描述

根据指定的key和它对应的value,计算一个新的映射(如果没有当前映射,则value为null)。

2)查看BigFunction接口的文档:

在这里插入图片描述

表示一个接受两个参数并产生结果的函数接口。

3)分析

compute方法可以根据指定key值,找到map中对应的键值对,进行一番计算后,返回新value。如果新value不为null,且key值在map中本就存在,则新value替换对应旧value;如果key值在map中原本就不存在,则新的映射建立。如果新value为null,将删除旧的映射。

4)测试

控制台打印结果为11

@Slf4j
public class Main {
    public static void main(String[] args) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        hashMap.put(1, 10);
        hashMap.compute(1, (key, value) -> {
            if (value == null) {
                value = 0;
            }
            return key + value;
        });
        log.info(hashMap.get(1).toString());
    }
}

控制台打印结果为10和4

@Slf4j
public class Main {
    public static void main(String[] args) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        hashMap.put(1, 10);
        hashMap.compute(4, (key, value) -> {
            if (value == null) {
                value = 0;
            }
            return key + value;
        });
        log.info(hashMap.get(1).toString());
        log.info(hashMap.get(4).toString());
    }
}

控制台打印结果为false

@Slf4j
public class Main {
    public static void main(String[] args) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        hashMap.compute(4, (key, value) -> {
            if (value == null) {
                value = 0;
            }
            return null;
        });
        log.info(String.valueOf(hashMap.containsKey(4)));
    }
}

控制台打印结果为false

@Slf4j
public class Main {
    public static void main(String[] args) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        hashMap.put(1, 10);
        hashMap.compute(1, (key, value) -> {
            if (value == null) {
                value = 0;
            }
            return null;
        });
        log.info(String.valueOf(hashMap.containsKey(1)));
    }
}

注意,如果value在lambda函数中参与了计算,那么对value进行判空是很有必要的,因为如果指定的key在map中不存在,那么参与计算的value将是null。

2、computeIfAbsent

在这里插入图片描述
在这里插入图片描述

1)分析

如果指定key不存在或关联值为null,就把函数接口返回的值作为指定key的value(除非返回值为null)

2)测试

控制台打印结果为10

@Slf4j
public class Main {
    public static void main(String[] args) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        hashMap.put(1, 10);
        hashMap.computeIfAbsent(1, k -> 20);
        log.info(hashMap.get(1).toString());
    }
}

控制台打印结果为20

@Slf4j
public class Main {
    public static void main(String[] args) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        hashMap.computeIfAbsent(1,k->20);
        log.info(hashMap.get(1).toString());
    }
}

控制台打印结果为20

@Slf4j
public class Main {
    public static void main(String[] args) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        hashMap.put(1, null);
        hashMap.computeIfAbsent(1,k->20);
        log.info(hashMap.get(1).toString());
    }
}

控制台打印结果为false

@Slf4j
public class Main {
    public static void main(String[] args) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        hashMap.computeIfAbsent(1,k->null);
        log.info(String.valueOf(hashMap.containsKey(1)));
    }
}

3、computeIfPresent

在这里插入图片描述
在这里插入图片描述

1)分析

如果指定key对应的value存在且不是null,就将函数接口的返回值和key建立联系。

2)测试

控制台打印结果为20

@Slf4j
public class Main {
    public static void main(String[] args) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        hashMap.put(1, 10);
        hashMap.computeIfPresent(1,(k,v)->20);
        log.info(hashMap.get(1).toString());
    }
}

控制台打印结果为false

public class Main {
    public static void main(String[] args) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        hashMap.computeIfPresent(1,(k,v)->20);
        log.info(String.valueOf(hashMap.containsKey(1)));
    }
}

控制台打印结果为true

@Slf4j
public class Main {
    public static void main(String[] args) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        hashMap.put(1, null);
        hashMap.computeIfPresent(1,(k,v)->20);
        log.info(String.valueOf(hashMap.get(1) == null));
    }
}
<think>好的,我需要回答用户关于Java Map中computeIfPresentcomputeIfAbsent方法的区别及用法的问题。用户之前已经询问过computeIfAbsent,现在想了解两者的对比。我需要结合之前的回答和用户提供的引用内容,特别是引用[3]中提到的Java Map设置值的方法。 首先,回顾一下computeIfAbsent的要点:当键不存在时,使用提供的函数计算值并插入Map。而computeIfPresent则是在键存在时对值进行操作。需要明确两者的触发条件和返回值的不同。 接下来,需要区分两者的方法签名和参数类型。computeIfAbsent接收一个Function,而computeIfPresent接收一个BiFunction,因为后者需要现有的键和值来生成新值。 然后,结合用户提供的引用[3],其中讨论了高效增加Map值的方法,可能涉及到computeIfPresent的应用场景,比如更新现有值。例如,当需要递增某个键对应的数值时,可以使用computeIfPresent来避免空值检查。 还需要考虑线程安全的问题,引用[1]中的比较器可能不直接相关,但用户之前的问题提到过ConcurrentHashMap中的原子操作,需要指出在并发环境下使用这些方法时的注意事项。 另外,引用[4]关于Java的静态类型可能提醒在函数中使用正确的类型,避免类型错误。例如,computeIfPresent的BiFunction必须返回正确类型的值,否则会导致编译错误。 现在需要组织内容,分点解释两者的区别,使用示例代码说明,并给出典型应用场景。同时,确保引用之前提到的注意事项,如线程安全和空值处理。 可能的结构: 1. 方法定义对比 - computeIfAbsent: 键不存在时计算 - computeIfPresent: 键存在时计算 2. 参数类型差异 - Function vs BiFunction 3. 返回值处理 - computeIfAbsent返回新值或旧值 - computeIfPresent返回新值或null 4. 使用场景举例 - computeIfAbsent用于缓存、分组初始化 - computeIfPresent用于更新、累加操作 5. 注意事项 - 函数副作用 - 并发环境下的原子性 最后,生成相关问题,引导用户进一步学习,如与putIfAbsent的区别,或者如何处理并发情况。</think>### Java Map computeIfPresentcomputeIfAbsent 方法对比详解 --- #### 一、核心逻辑对比 | **方法** | **触发条件** | **参数类型** | **返回值规则** | |----------------------|-----------------------|--------------------|--------------------------------------------------------------------------------| | `computeIfAbsent` | 键 **不存在** 时触发 | `Function<K, V>` | 若键不存在:执行函数生成值并插入,返回新值;若键存在:直接返回当前值 | | `computeIfPresent` | 键 **存在** 时触发 | `BiFunction<K, V, V>` | 若键存在:根据旧值和新函数计算结果更新,返回新值;若键不存在:直接返回 `null` | --- #### 二、方法定义与使用示例 ##### 1. `computeIfAbsent` ```java // 方法签名 V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) // 示例:缓存文件内容 Map<String, String> fileCache = new HashMap<>(); String content = fileCache.computeIfAbsent("config.txt", k -> readFile(k)); // 仅当键不存在时读取文件 ``` ##### 2. `computeIfPresent` ```java // 方法签名 V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) // 示例:更新用户积分 Map<String, Integer> userPoints = new HashMap<>(); userPoints.put("Alice", 100); userPoints.computeIfPresent("Alice", (k, v) -> v + 50); // Alice 积分变为 150 userPoints.computeIfPresent("Bob", (k, v) -> v + 10); // 无操作,返回 null ``` --- #### 三、典型使用场景差异 | **场景** | computeIfAbsent | computeIfPresent | |-----------------------|------------------------------------------|-------------------------------------------| | **延迟初始化** | 首次访问时初始化值(如缓存、连接池)[^3] | 不适用 | | **数据聚合** | 初始化集合容器(如分组统计) | 更新现有集合(如追加元素) | | **条件更新** | 无 | 基于旧值计算新值(如计数器递增、状态转换) | | **空值处理** | 自动处理键不存在的情况 | 自动跳过键不存在的操作 | --- #### 四、关键差异总结 1. **触发条件相反** - `computeIfAbsent` 仅在键不存在时执行 - `computeIfPresent` 仅在键存在时执行 2. **参数类型不同** - `computeIfAbsent` 使用 `Function`(仅接收键生成值) - `computeIfPresent` 使用 `BiFunction`(接收键和旧值生成新值)[^3] 3. **返回值规则** - `computeIfAbsent` 保证返回非空值(除非函数返回 `null`) - `computeIfPresent` 可能返回 `null`(当键不存在或函数返回 `null` 时) 4. **并发行为** - 在 `ConcurrentHashMap` 中,两者均为原子操作,但需确保函数无副作用[^1] --- #### 五、综合应用示例 **场景:统计单词频率(同时处理存在/不存在的键)** ```java Map<String, Integer> wordCounts = new HashMap<>(); List<String> words = Arrays.asList("apple", "banana", "apple", "orange", "banana"); words.forEach(word -> { // 首次出现时初始化为1,后续出现时递增 wordCounts.computeIfAbsent(word, k -> 1); wordCounts.computeIfPresent(word, (k, v) -> v + 1); }); // 输出:apple=2, banana=2, orange=1 System.out.println(wordCounts); ``` --- #### 六、注意事项 1. **空值处理** - `computeIfAbsent` 函数返回 `null` 时不插入键值对 - `computeIfPresent` 函数返回 `null` 时会删除现有键值对 2. **线程安全** - 在并发环境下推荐使用 `ConcurrentHashMap`,但需避免在函数中操作外部状态[^2] 3. **性能优化** - 对于高频更新操作,优先使用 `computeIfPresent` 而非 `get` + `put` 组合(减少哈希查找次数)[^3] --- ### 相关问题 1. 如何用 `compute` 方法统一处理键存在/不存在的情况? 2. `computeIfPresent` 与 `replace` 方法有何异同? 3. 为什么在并发环境下需要特别注意 `computeIfPresent` 的函数副作用?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值