Map中的put、putIfAbsent、compute、computeIfAbsent、computeIfPresent使用方法

目录

1.概述

2.内容    

 2.1 put:【添加】

 2.2 putIfAbsent:【不存在则添加】

 2.3 compute:【计算】

 2.4 computeIfAbsent:【不存在则计算】

 2.5 computeIfPresent:【存在则计算】


1.概述


        Map是做软件开发中使用频率最高的数据结构之一。它的最大特点是能够快速地进行查找和更改数据,经常被用做缓存以及数据重复检查场景中。很多人在给Map赋值过程中只知道最基础的put方法,下面我将给大家详细介绍下其他赋值方法的区别。

2.内容

 2.1 put:【添加】

key不存在,直接添加,put方法返回null;

key存在,新值覆盖旧值,put方法返回旧值。

public static void main(String[] args) {
    Map<Integer, String> map = new HashMap<>();
    System.out.println("1->"+map.put(1, "a"));
    System.out.println("2->"+map.put(2, "b"));
    System.out.println("3->"+map.put(2, "c"));
    System.out.println("4->"+map);
    //控制台输出:
//    1->null
//    2->null
//    3->b
//    4->{1=a, 2=c}
}

 2.2 putIfAbsent:【不存在则添加】

key不存在,直接添加,putIfAbsent方法返回null;

key存在,不进行操作,putIfAbsent方法返回旧值。

public static void main(String[] args) {
    Map<Integer, String> map = new HashMap<>();
    System.out.println("1->"+map.putIfAbsent(1, "a"));
    System.out.println("2->"+map.putIfAbsent(2, "b"));
    System.out.println("3->"+map.putIfAbsent(2, "c"));
    System.out.println("4->"+map);
    //控制台输出:
//    1->null
//    2->null
//    3->b
//    4->{1=a, 2=b}
}

 2.3 compute:【计算】

计算后结果为null,删除当前key,compute方法返回null;

计算后结果不为null,分为以下两种情况:

        key不存在,直接添加,compute方法返回新值;

        key存在,新值覆盖旧值,compute方法返回新值。

public static void main(String[] args) {
    Map<Integer, String> map = new HashMap<>();
    System.out.println("1->"+map.compute(1, (k, v) ->  k + "a"));
    System.out.println("1->"+map);
    System.out.println("2->"+map.compute(1, (k, v) ->  k + "b"));
    System.out.println("2->"+map);
    System.out.println("3->"+map.compute(1, (k, v) -> null));
    System.out.println("3->"+map);
    System.out.println("4->"+map.compute(2, (k, v) ->  null));
    System.out.println("4->"+map);
    //控制台输出:
//    1->1a
//    1->{1=1a}
//    2->1b
//    2->{1=1b}
//    3->null
//    3->{}
//    4->null
//    4->{}
}

 2.4 computeIfAbsent:【不存在则计算】

key不存在,直接添加,computeIfAbsent方法返回新值;

key存在,不进行操作,computeIfAbsent方法返回旧值;

计算为null,不进行操作,key存在时computeIfAbsent返回旧值,不存在时返回null。

使用场景:

map值为列表时,如果是个不存在的key,则需要先初始化一个列表;如果是个存在的key,则从map中取到此列表。最后给列表添加元素,再把列表放入到map中。

如果用computeIfAbsent,则可以直接获取list而不需要判断key存不存在。

Map<String,List<String>> map = new HashMap<>();
List<String> list=map.computeIfAbsent(mapKey, k -> new ArrayList<>());
list.add("a");
map.put(mapKey,list);

public static void main(String[] args) {
    Map<Integer, String> map = new HashMap<>();
    System.out.println("1->"+map.computeIfAbsent(1, k ->  k + "a"));
    System.out.println("1->"+map);
    System.out.println("2->"+map.computeIfAbsent(1, k ->  k + "b"));
    System.out.println("2->"+map);
    System.out.println("3->"+map.computeIfAbsent(1, k -> null));
    System.out.println("3->"+map);
    System.out.println("4->"+map.computeIfAbsent(2, k ->  null));
    System.out.println("4->"+map);
    //控制台输出:
//    1->1a
//    1->{1=1a}
//    2->1a
//    2->{1=1a}
//    3->1a
//    3->{1=1a}
//    4->null
//    4->{1=1a}
}

 2.5 computeIfPresent:【存在则计算】

计算后结果为null,删除当前key,compute方法返回null;

计算后结果不为null,分为以下两种情况:

        key不存在,不进行操作,computeIfPresent方法返回null;

        key存在,新值覆盖旧值,computeIfPresent方法返回新值;

public static void main(String[] args) {
    Map<Integer, String> map = new HashMap<>();
    System.out.println("1->"+map.computeIfPresent(1, (k, v) ->  k + "a"));
    System.out.println("1->"+map);
    map.put(1,"A");
    System.out.println("2->"+map.computeIfPresent(1, (k, v) ->  k + "b"));
    System.out.println("2->"+map);
    System.out.println("3->"+map.computeIfPresent(1, (k, v) -> null));
    System.out.println("3->"+map);
    System.out.println("4->"+map.computeIfPresent(2, (k, v) -> null));
    System.out.println("4->"+map);
    //控制台输出:
//    1->null
//    1->{}
//    2->1b
//    2->{1=1b}
//    3->null
//    3->{}
//    4->null
//    4->{}
}

<think>好的,我需要回答用户关于Java MapcomputeIfPresentcomputeIfAbsent方法的区别及用法的问题。用户之前已经询问过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` 的函数副作用?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值