gpt4 book ai didi

java - 关于效率: .filter(Optional::isPresent).map(Optional::get) 不是比 .flatmap(Optional::stream) 更好吗?

转载 作者:行者123 更新时间:2023-12-03 18:20:33 26 4
gpt4 key购买 nike

Optional::stream如果存在,则返回一个包含该值的 Stream,否则返回一个空流。所以对于 Stream<Optional<T>> optionals ,

optionals.flatMap(Optional::stream)
返回 Stream<T>包含所有选项的现值。但是关于它背后的功能,我不确定为每个当前值创建一个自己的流然后 flatMapping 流的流的效率有多高。
但即使在 documentation这被称为预期用途。
与首先过滤所有当前值然后将选项映射到它们的值相比,为什么流式传输选项流不是非常低效?

最佳答案

我很好奇并使用 JMH 编写了一个简单的微基准测试。 .
事实证明,使用 flatMap(Optional::stream) 会造成相当严重的性能损失。在 filter(Optional::isPresent).map(Optional::get) .
Java 16 引入 mapMulti类似于 flatMap在使用中并且具有非常接近于 filter 的性能特征/map .
我的每个基准测试方法都需要一个列表 Optional<Integer>并计算所有现值的总和。
我实现了三种方法:

  • flatMap 正如提出的问题
  • filter map 如问题
  • 中所述
  • mapMulti 在 JDK 16 中引入。

  • 请注意,我做了 不是 使用 flatMapToIntmapMultiToInt方法,这可能更有效,因为我不想关注流包装器对象方面,而只是比较 Optional 上流的使用情况。对象。
    对于所有方法,我使用完整列表(所有值都存在)、半空列表(每隔一个值存在)和完全空列表(每个 optional 值都为空)来运行基准测试。列表的长度都相同(每个列表任意选取 10 000 个元素)。
    值的单位是 us/op(每个操作的微秒,意味着一个完整的流评估)。


    方法
    完整列表
    半空列表
    空列表

    flatMap207.219±1.176
    175.355±4.955
    142.986±2.821
    filter/ map12.856±0.375
    12.086±0.451
    6.856±0.143
    mapMulti13.990±0.353
    11.685±0.276
    7.034±0.199


    请注意,这里的绝对数字特定于我运行 JDK 16 的机器,无论如何都无关紧要。相对差异在这里很重要。
    看来 flatMap方法既慢得多,又多变。如果我不得不猜测可变性来自所有 Stream 引起的 GC 压力增加创建的对象,即使是空结果。
    免责声明 :这显然只是一个正在测试的虚构示例,并且基准测试尚未经过同行评审(尚未),因此不要在没有进一步调查的情况下将这些结果视为理所当然。
    下面是完整的基准代码(请注意,我拒绝了一些迭代/运行时间以在合理的时间内获得响应并硬编码为使用 4 个线程。根据需要进行调整。)
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Optional;
    import java.util.concurrent.TimeUnit;

    @Fork(value = 1, warmups = 0)
    @Warmup(iterations = 5, time = 5)
    @Measurement(iterations = 5, time = 5)
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    @Threads(4)
    public class MyBenchmark {

    @State(Scope.Benchmark)
    public static class MyLists {
    private static final int LIST_SIZE = 10_000;

    public final List<Optional<Integer>> allValues;
    public final List<Optional<Integer>> halfEmpty;
    public final List<Optional<Integer>> allEmpty;

    public MyLists() {
    List<Optional<Integer>> allValues = new ArrayList<>(LIST_SIZE);
    List<Optional<Integer>> halfEmpty = new ArrayList<>(LIST_SIZE);
    List<Optional<Integer>> allEmpty = new ArrayList<>(LIST_SIZE);
    for (int i = 0; i < LIST_SIZE; i++) {
    Optional<Integer> o = Optional.of(i);
    allValues.add(o);
    halfEmpty.add(i % 2 == 0 ? o : Optional.empty());
    allEmpty.add(Optional.empty());
    }
    this.allValues = Collections.unmodifiableList(allValues);
    this.halfEmpty = Collections.unmodifiableList(halfEmpty);
    this.allEmpty = Collections.unmodifiableList(allEmpty);
    }
    }

    @Benchmark
    public long filter_and_map_allValues(MyLists lists) {
    return filterAndMap(lists.allValues);
    }

    @Benchmark
    public long filter_and_map_halfEmpty(MyLists lists) {
    return filterAndMap(lists.halfEmpty);
    }

    @Benchmark
    public long filter_and_map_allEmpty(MyLists lists) {
    return filterAndMap(lists.allEmpty);
    }

    @Benchmark
    public long flatMap_allValues(MyLists lists) {
    return flatMap(lists.allValues);
    }

    @Benchmark
    public long flatMap_halfEmpty(MyLists lists) {
    return flatMap(lists.halfEmpty);
    }

    @Benchmark
    public long flatMap_allEmpty(MyLists lists) {
    return flatMap(lists.allEmpty);
    }


    @Benchmark
    public long mapMulti_allValues(MyLists lists) {
    return mapMulti(lists.allValues);
    }

    @Benchmark
    public long mapMulti_halfEmpty(MyLists lists) {
    return mapMulti(lists.halfEmpty);
    }

    @Benchmark
    public long mapMulti_allEmpty(MyLists lists) {
    return mapMulti(lists.allEmpty);
    }

    private long filterAndMap(List<Optional<Integer>> input) {
    return input.stream().filter(Optional::isPresent).map(Optional::get).mapToInt(Integer::intValue).sum();
    }

    private long flatMap(List<Optional<Integer>> input) {
    return input.stream().flatMap(Optional::stream).mapToInt(Integer::intValue).sum();
    }

    private long mapMulti(List<Optional<Integer>> input) {
    // Unfortunately the type witness <Integer> is necessary here, as type inference would otherwise make mapMulti produce a Stream<Object>.
    return input.stream().<Integer>mapMulti(Optional::ifPresent).mapToInt(Integer::intValue).sum();
    }
    }

    关于java - 关于效率: .filter(Optional::isPresent).map(Optional::get) 不是比 .flatmap(Optional::stream) 更好吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66698940/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com