您当前的位置:首页 > 互联网教程

Java8 Map中的computeIfAbsent方法

发布时间:2025-05-20 04:51:52    发布人:远客网络

Java8 Map中的computeIfAbsent方法

一、Java8 Map中的computeIfAbsent方法

1、Map接口的实现类,如HashMap、ConcurrentHashMap和HashTable等,继承了computeIfAbsent方法。此方法的引入,旨在简化代码逻辑,使其更具简洁性。

2、首先,我们来了解computeIfAbsent方法的使用场景。当需要在Map中存储某个特定的key值,但该key对应的value在映射函数中计算得出时,并且希望只在不存在该key时才执行计算逻辑。这时,computeIfAbsent方法便显得尤为实用。

3、通过此方法,Map会首先检查缓存中是否存在指定key的值。如果不存在,则会调用mappingFunction(key)来计算key对应的value。随后,将计算出的value与key一同存储到缓存Map中。

4、值得注意的是,若mappingFunction(key)的返回值为null或抛出异常,则不会在Map中记录任何内容。

5、利用Java 8的特性,我们可以通过一行代码简洁地实现上述功能,使代码逻辑更加清晰,易于理解和维护。

二、...| 慎用 Java 8 ConcurrentHashMap 的 computeIfAbsent

前言

我们先看一段代码,代码中使用 Map的时候,有可能会这么写:

我们先看一段代码,代码中使用 Map的时候,有可能会这么写:

Java 8的 java.util.Map里面有个方法 computeIfAbsent,能够简化以上代码:

以上这种写法除了简洁,如果使用的是 java.util.concurrent.ConcurrentHashMap,还能够在并发调用的情况下确保 calculateValue方法不会被重复调用,保证原子性。

不过,前段时间对 Apache ShardingSphere-Proxy做压测时遇到一个问题,当 BenchmarkSQL连接 ShardingSphere Proxy的 Terminal数量比较高时,其中一条很简单的插入 SQL执行延迟增加了很多。借助 Async Profiler发现 Java 8 ConcurrentHashMap的 computeIfAbsent在性能上有坑。

不了解 Apache ShardingSphere的读者可以参考 。

考虑到当时的压测的现象是 BenchmarkSQL并发数(Terminals)越高,New Order业务中一条简单且重复执行的 insert SQL执行延时越长。但是 ShardingSphere-Proxy的所在机器的 CPU也没有压满,考虑是不是 Proxy代码层面存在瓶颈,于是借助 async-profiler对压测状态下的 Proxy JVM采样。

关于 async-profiler可以参考 ,后续我也考虑写一些相关文章。

使用 IDEA读取采样获得的 jfr文件,看到 Java Monitor Blocked事件居然有三百多万次!根据堆栈,找到 ShardingSphere这段使用了 computeIfAbsent代码,以下为节选:

以上这段代码在每一次 Proxy与数据库交互前都会执行,即通过 Proxy执行 CRUD操作的必经之路,而且里面的 type目前只有 2种,分别是 JDBC.STATEMENT和 JDBC.PREPARED_STATEMENT,所以在高并发的情况下会有大量的线程调用同一个 key的 computeIfAbsent。

我的理解是,如果在 key存在的情况下,computeIfAbsent操作就不存在修改的情况了,直接 get出来就好,那事实如何?看一下 computeIfAbsent方法的实现(JDK是 Oracle 8u311),节选代码并加了一些注释:

根据我对源码的理解,即使 key存在,computeIfAbsent去找 key的时候,都会进入 synchronized代码。那这相比 ConcurrentHashMap不加锁的 get操作不就影响性能了吗?Google一下相应的话题,发现了一些内容: 这个问题早就有人提过了,也在 JDK 9处理了。截至本文编写 JDK 17已经正式发布了。

在目前 JDK 8仍然盛行的环境下,我们有必要考虑如何避免上面的问题,于是相应的处理方法就诞生了:

每次从 Map中获取 value前,都先用 get做一次检查,value不存在才使用 computeIfAbsent放入 value。由于 ConcurrentHashMap的 computeIfAbsent可以保证操作原子性,这里也不需要自己加 synchronized或者做多重检查之类的操作。

三、用java求1到30 的阶层和 用方法

1、这个公式可以通过一个java的循环来实现,也就是第一种方法。

2、这个循环在i=0的时候结束,而阶乘的结果通过result*=i来计算。

3、public static int compute(int number)

4、 for(int i=number;i>0;i--){

5、 public static void main(String[] args)

6、 System.out.println(compute(30));//789912231223232323211

7、递归就是方法调用自身,最终通过一个出口来结束程序的调用。

8、这个出口要保证程序不出现死循环。

9、在上面的f(x)- n*f(x-1)这里,出口就可以设置为

10、public static int cur(int number){