computeIfAbsent的使用

Map中computeIfAbsent的使用

HashMap.computeIfAbsent

如果需要向Map中push一个键值对,需要判断K key在当前map中是否已经存在,不存在则通过后面的 Function<? super K, ? extends V> mappingFunction 来进行value计算,且将结果当作value同key一起push到Map中。

ConcurrentHashMap.computeIfAbsent

ConcurrentHashMap的读写方法具有原子性(pushall是聚合方法,不具有原子性),所以它的computeIfAbsent方法可以在高并发环境下稳定运行。这样避免了使用synchronized,同时可以在高并发下保证一定的性能。

这是因为ConcurrentHashMap.computeIfAbsent是通过 Java 自带的 Unsafe 实现的 CAS。它在虚拟机层面确保了写入数据的原子性,这相对于加锁的效率,会高得多。

常量的声明


    //线程数
    private static int THREAD_COUNT = 8;
    //map的大小
    private static int ITEM_COUNT = 8;
    //共处理多少次数据
    private static int LOOP_COUNT = 10000000;

常见的写法:

    private static Map<String, Long> normaledWriting() throws InterruptedException {
        ConcurrentHashMap<String, Long> freqs = new ConcurrentHashMap<>(ITEM_COUNT);
        ForkJoinPool forkJoinPool = new ForkJoinPool(THREAD_COUNT);
        forkJoinPool.execute(() -> IntStream.rangeClosed(1, LOOP_COUNT).parallel().forEach(i -> {
                    String key = "item" + ThreadLocalRandom.current().nextInt(ITEM_COUNT);
                    synchronized (freqs) {
                        if (freqs.containsKey(key)) {
                            freqs.put(key, freqs.get(key) + 1);
                        } else {
                            freqs.put(key, 1L);
                        }
                    }
                }
        ));
        forkJoinPool.shutdown();
        //设置最大等待时间
        forkJoinPool.awaitTermination(20, TimeUnit.MINUTES);
        return freqs;
    }

高效率的写法:

    private static Map<String, Long> recommendedWriting() throws InterruptedException {
        ConcurrentHashMap<String, LongAdder> freqs = new ConcurrentHashMap<>(ITEM_COUNT);
        ForkJoinPool forkJoinPool = new ForkJoinPool(THREAD_COUNT);
        forkJoinPool.execute(() -> IntStream.rangeClosed(1, LOOP_COUNT).parallel().forEach(i -> {
                    String key = "item" + ThreadLocalRandom.current().nextInt(ITEM_COUNT);
                    //如果key是null,则赋给value默认的LongAdder
                    //key每出现一次 给value+1
                    freqs.computeIfAbsent(key, k -> new LongAdder()).increment();
                }
        ));
        forkJoinPool.shutdown();
        forkJoinPool.awaitTermination(20, TimeUnit.MINUTES);
        return freqs.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        e -> e.getValue().longValue())
                );
    }

接下来通过java计时器来检测比较效率


    public static void main(String[] args) throws InterruptedException {
        //添加java计时器,用于比较使用普通的
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("normaledWriting");
        Map<String, Long> normaluse = normaledWriting();
        stopWatch.stop();
        Assert.isTrue(normaluse.size() == ITEM_COUNT, "normaluse size error");
        Assert.isTrue(normaluse.values().stream()
                        .mapToLong(l -> l).reduce(0, Long::sum) == LOOP_COUNT
                , "normaledWriting count error");
        stopWatch.start("recommendedWriting");
        Map<String, Long> gooduse = recommendedWriting();
        stopWatch.stop();
        Assert.isTrue(gooduse.size() == ITEM_COUNT, "gooduse size error");
        Assert.isTrue(gooduse.values().stream()
                        .mapToLong(l -> l)
                        .reduce(0, Long::sum) == LOOP_COUNT
                , "recommendedWriting count error");
        log.info(stopWatch.prettyPrint());
    }

最终运行效果
在这里插入图片描述
相关引入包

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import org.springframework.util.StopWatch;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

ORCALE ConcurrentHashMap 的官方文档

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`computeIfAbsent()` 是一个 Java 8 新增的方法,用于在 `Map` 中根据 Key 获取 Value,如果 Value 不存在,则可以通过提供的计算函数进行计算并将计算结果存储到 Map 中。该方法的使用示例如下: ```java Map<String, Integer> map = new HashMap<>(); map.computeIfAbsent("A", k -> 1); map.computeIfAbsent("B", k -> 2); map.computeIfAbsent("C", k -> 3); System.out.println(map.get("A")); // 1 System.out.println(map.get("B")); // 2 System.out.println(map.get("C")); // 3 System.out.println(map.get("D")); // null ``` 上述示例中,首先创建了一个空的 HashMap 对象 `map`,然后通过 `computeIfAbsent()` 方法将 Key-Value 存储到 Map 中。其中,计算函数通过 Lambda 表达式实现,如果指定 Key 对应的 Value 已经存在,则不会进行计算,只会返回已有的 Value 值。最后,通过 `get()` 方法获取指定 Key 对应的 Value 值。 需要注意的是,`computeIfAbsent()` 方法仅在 Value 不存在时才会进行计算,如果 Value 已经存在,则不会进行任何操作。此外,在计算函数中也可以根据 Key 和已有的 Value 进行计算,例如: ```java Map<String, Integer> map = new HashMap<>(); map.computeIfAbsent("A", k -> 1); map.computeIfAbsent("B", k -> k.length()); System.out.println(map.get("A")); // 1 System.out.println(map.get("B")); // 1 ``` 上述示例中,计算函数中使用了 Key 和已有的 Value 进行计算,如果 Value 已经存在,则使用已有的 Value 进行计算。因此,对于 Key "B",计算函数返回值为 `1`,而不是 `2`。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值