Java 8流:折叠/抽象流部分

12

假设我有这个流:

list.stream()
    .map(fn1)      // part1
    .map(fn2)      // 
    .filter(fn3)   // 

    .flatMap(fn4)  // part 2 
    .map(fn5)      //
    .filter(fn6)   //
    .map(fn7)      //
    .collect(Collectors.toList())

我该如何让它看起来像:

list.stream()
    .map(fnPart1)      
    .map(fnPart2)
    .collect(Collectors.toList())

为了维护的原因,我不想手动解除 fnX 部分并将它们组合在一起,而是要保持它们不变,并用它们来表达 fnPartX。


3
与 flatMap 和 filter 不同,map 不能改变流中项目的数量。 - David Conrad
1
@BenoitParis 我可能误解了问题,但您可以将part1分配给Stream<String>,将part2分配给Stream<Object>,然后在Stream<Object>上调用collect吗? - Chetan Kinger
3
抱歉,您想要的方式可能无法实现(使用两个函数,在初始流上使用map)。这是因为map()函数作用于流的元素(它转换流的每个元素,而不是流本身),所以您不能筛选元素,这是涉及到流本身的操作(基于条件删除一些元素)。 - fps
1
第一部分和第二部分是固定的吗?我的意思是,它们总是相同的吗?比如,part1是map(fn1),然后是map(fn2),最后是filter(fn3) - fps
2个回答

14

您可以使用函数来表达和组合它:

Function<Stream<T1>, Stream<T2>> fnPart1 = 
        s -> s.map(fn1)
            .map(fn2) 
            .filter(fn3);
Function<Stream<T2>, Stream<T3>> fnPart2 = 
        s -> s.flatMap(fn4)
            .map(fn5)      
            .filter(fn6)   
            .map(fn7);

fnPart1.andThen(fnPart2).apply(list.stream()).collect(Collectors.toList());

函数的输入和输出类型必须相应匹配。

这可以成为更复杂的组合结构的基础,例如:

public class Composer<T>{
    private final T element;

    private Composer(T element){
        this.element = element;
    }

    public <T2> Composer<T2> andThen(Function<? super T, ? extends T2> f){
        return new Composer<>(f.apply(element));
    }

    public T get(){
        return element;
    }

    public static <T> Composer<T> of(T element){
        return new Composer<T>(element);
    }
}

这可以像这样使用:
Composer.of(list.stream())
    .andThen(fnPart1)
    .andThen(fnPart2)
    .get()
    .collect(Collectors.toList());

3
“Composer”可能需要更通用,以便不仅可用于流。这让我想起了“Optional”。 - 4castle
4
你说得对。Composer不使用任何与流相关的属性 - 我已经编辑过了。这只是一个试图将函数的组合结构带入到与OP想要的类似的结构中的尝试。 - Calculator
谢谢!这正是我想要的。我一直在担心需要实现中间的东西,但 andThen 让我们组合函数。 - BenoitParis

5

您需要使用flatMap而不是map。我不知道您的类型是什么,所以我将它们称为T1、T2等等。

list.stream()
    .flatMap(fnPart1)      
    .flatMap(fnPart2)
    .collect(Collectors.toList())

Stream<T2> fnPart1(T1 t1) {
    return Stream.of(t1).map(fn1).map(fn2).filter(fn3);
}

Stream<T3> fnPart2(T2 t2) {
    return Stream.of(t2).flatMap(fn4).map(fn5).filter(fn6).map(fn7);
}

当然,您可以删除一些流操作:
Stream<T2> fnPart1(T1 t1) {
    return Stream.of(fn2(fn1(t1))).filter(fn3);
}

Stream<T3> fnPart2(T2 t2) {
    return fn4(t2).map(fn5).filter(fn6).map(fn7);
}

进一步简化是可能的,因为fnPart1和fnPart2只涉及单个元素。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接