从Java 16开始,何时以及如何在flatMap上执行一到0..n映射Stream mapMulti

从Java 16开始,何时以及如何在flatMap上执行一到0..n映射Stream mapMulti,java,java-stream,flatmap,java-16,mapmulti,Java,Java Stream,Flatmap,Java 16,Mapmulti,我浏览了新闻和Java16的源代码,遇到了名为mapMulti的新流方法。早期的访问称它类似于flatMap,并且已经被批准使用相同的Java版本 <R> Stream<R> mapMulti​(BiConsumer<? super T,​? super Consumer<R>> mapper) 流映射多​(BiConsumerStream::mapMulti是一种新方法,被归类为中间操作 它需要一个双消费者映射器将要处理的元素作为消费者。后者使

我浏览了新闻和Java16的源代码,遇到了名为
mapMulti
的新流方法。早期的访问称它类似于
flatMap
,并且已经被批准使用相同的Java版本

<R> Stream<R> mapMulti​(BiConsumer<? super T,​? super Consumer<R>> mapper)

流映射多​(BiConsumer
Stream::mapMulti
是一种新方法,被归类为中间操作

它需要一个
双消费者映射器
将要处理的元素作为
消费者
。后者使该方法乍看起来很奇怪,因为它不同于我们在其他中间方法中使用的方法,例如
映射
过滤器
,或
窥视
,其中没有任何一种方法
*消费者的变化

API本身在lambda表达式中提供的
使用者
的目的是接受后续管道中可用的任意数量的元素。因此,所有元素,无论有多少,都将被传播

使用简单片段的解释
  • 一对一些(0..1)映射(类似于
    过滤器

    仅对少数选定的项目使用
    使用者.accept(R)
    可实现类似筛选器的管道。这在根据谓词检查元素并将其映射到不同值的情况下可能会很有用,否则将使用
    筛选器
    映射
    的组合来完成。下面是

    Stream.of(“Java”、“Python”、“JavaScript”、“C#”、“Ruby”)
    .mapMulti((str,消费者)->{
    如果(str.length()>4){
    consumer.accept(str.length());//长度大于4
    }
    })
    .forEach(i->System.out.print(i+);
    // 6 10
    
  • 一对一映射(类似于
    映射

    使用上一个示例,如果省略了条件,并且每个元素都映射到一个新元素中,并使用
    使用者
    接受,则该方法的有效行为类似于
    映射

    Stream.of(“Java”、“Python”、“JavaScript”、“C#”、“Ruby”)
    .mapMulti((str,consumer)->consumer.accept(str.length())
    .forEach(i->System.out.print(i+);
    // 4 6 10 2 4
    
  • 一对多映射(类似于
    flatMap

    这里的事情变得有趣,因为人们可以调用
    consumer.accept(R)
    任意次数。假设我们想要复制表示字符串长度的数字本身,即
    2
    变成
    2
    2
    4
    变成
    4
    4
    4
    4
    0
    变成零

    Stream.of(“Java”、“Python”、“JavaScript”、“C#”、“Ruby”、“Ruby”)
    .mapMulti((str,消费者)->{
    对于(int i=0;iSystem.out.print(i+);
    // 4 4 4 4 6 6 6 6 6 6 10 10 10 10 10 10 10 10 10 10 2 2 4 4 4 4 
    
与flatMap的比较 该机制的核心思想是可以多次调用(包括零次)与
flatMap
不同,它在内部使用
SpinedBuffer
允许将元素推入单个扁平流实例,而无需为每组输出元素创建新的流实例。使用此方法时,状态两个用例优于
flatMap

  • 使用少量(可能为零)元素替换每个流元素时。使用此方法可避免按flatMap的要求为每组结果元素创建新流实例的开销
  • 当使用命令式方法生成结果元素比以流的形式返回它们更容易时
就性能而言,在这种情况下,新方法
mapMulti
是赢家。请查看此答案底部的基准测试

过滤器映射方案 使用此方法而不是单独使用
过滤器
映射
是没有意义的,因为它太冗长,而且实际上创建了一个中间流。异常可能是替换
.filter(..).map(..)
chain一起调用,在检查元素类型及其类型时非常方便

int sum=1,2.0,3.0,4F,5,6L的流量
.MapMultiPoint((数字,消费者)->{
if(整数的数字实例){
consumer.accept((整数)编号);
}
})
.sum();
// 6
int sum=1,2.0,3.0,4F,5,6L的流量
.filter(数字->整数的数字实例)
.mapToInt(数字->(整数)数字)
.sum();
如上所述,引入了其变体,如和。这来自于原始流中的
mapMulti
方法,如。此外,引入了三个新的功能接口。基本上,它们是
BiConsumer
的原始变体,例如:

@FunctionalInterface
interface IntMapMultiConsumer {
    void accept(int value, IntConsumer ic);
}
组合真实用例场景 这种方法的真正威力在于其使用的灵活性,并且一次只创建一个流,这是比
flatMap
的主要优势。下面的两个片段表示
产品
及其
列表
0..n
提供的平面映射,由
提供
类表示,并基于证书在条件中(产品类别和变化可用性)

  • 产品
    带有
    字符串名称
    国际基价
    字符串类别
    列表变体
  • 变体
    带有
    字符串
    
    Benchmark                                   Mode  Cnt   Score   Error  Units
    MapMulti_FlatMap.flatMap                    avgt   25  73.852 ± 3.433  ns/op
    MapMulti_FlatMap.mapMulti                   avgt   25  17.495 ± 0.476  ns/op
    
    Benchmark                                   Mode  Cnt    Score  Error  Units
    MapMulti_FilterMap.filterMap                avgt   25   7.973 ± 0.378  ns/op
    MapMulti_FilterMap.mapMulti                 avgt   25   7.765 ± 0.633  ns/op 
    
    Benchmark                                   Mode  Cnt   Score   Error  Units
    MapMulti_FlatMap_Optional.flatMap           avgt   25  20.186 ± 1.305  ns/op
    MapMulti_FlatMap_Optional.mapMulti          avgt   25  10.498 ± 0.403  ns/op
    
    List<A> list = IntStream.range(0, r_i).boxed()
        .flatMap(i -> IntStream.range(0, r_j).boxed()
            .flatMap(j -> IntStream.range(0, r_k)
                .mapToObj(k -> new A(i, j, k))))
        .collect(Collectors.toList());