【问题标题】:Abstracting repeated Intermediate Stream-operations into a single Function将重复的中间流操作抽象为单个函数
【发布时间】:2022-10-24 22:43:49
【问题描述】:

我正在使用一个大型数据结构,我想在其中执行一系列模式的流操作:

<some stream>
    .map(<method reference getter that returns List>).filter(Objects::nonNull).flatMap(Collections::stream)
    .map(<method reference getter that returns another sub-List>).filter(Objects::nonNull).flatMap(Collections::stream)
    .forEach(<perform some operations>)

我想编写一个定义,将mapfilterflatMap 操作抽象为一个函数,我可以使用map 将其应用于流或将流传递给;在我看来,它看起来像这样:

private static final <T,R> BiFunction<Stream<T>, Function<T,List<R>>, Stream<R>> mapAndFlatten =
    (inStream, lstGetter) -> {
        return inStream.map(lstGetter)
            .filter(Objects::nonNull)
            .flatmap(Collections::stream);
}

但是,我没有正确地概念化一些事情。一方面,上述语法不正确;很明显我不能使用带有BiFunction 的泛型吗?是否有现有的框架/模式来完成我想要做的事情?组合构成后续map 操作的函数似乎很简单,那么添加filterflatMap 是否让我很难开发解决方案?我正在努力寻找有用的信息/示例。我是否以一种没有意义的方式将 OO 和功能概念混为一谈?

也许我为不需要存在的解决方案而努力工作;写.filter(Objects::nonNull).flatmap(Collections::stream) 并不难,但感觉很冗长。

【问题讨论】:

  • 您不能为变量或字段声明指定泛型参数。但是,您可以将整个字段/lambda 变成方法&lt;T,R&gt; Stream&lt;R&gt; mapAndFlatten(Stream&lt;T&gt; inStream, Function&lt;T, List&lt;R&gt;&gt; lstGetter) { ... },但您需要传递 Stream 本身。请记住,流操作中的 lambda 在单个元素,而不是整个流。此外,功能接口与方法签名同构(“相同”),实际上由方法签名指定。

标签: java functional-programming java-stream method-reference functional-interface


【解决方案1】:

很明显我不能使用带有 BiFunction 的泛型?

您可以将泛型与函数一起使用。但是您不能提供将流作为map() 操作的参数的函数,该操作需要Function&lt;? super T,? extends R&gt; 类型的参数。 IE。消耗一个流元素,不是一个溪流.而不是那个预期的类型是Function,而不是BiFunction

而且由于您正在展平数据,因此它不适用于map,因为map 旨在执行一对一转换(同时你有一对多转型)。

作为mapAndFlatten 的托管操作,您需要flatMap()mapMulti()

平面图()

flatMap() 需要一个函数使用流元素并生成结果类型 Function&lt;T, Stream&lt;R&gt;&gt; 的流。

这就是你如何生成这样的功能:

private static <T,R> Function<T, Stream<R>> mapAndFlatten(Function<T, List<R>> mapper) {
    
    return t -> Stream.of(t).map(mapper)
        .filter(Objects::nonNull)
        .flatMap(Collection::stream);
}

流看起来像这样:

Stream.of(t1, t2, t3)
    .flatMap(mapAndFlatten(Method_Ref_1))
    .flatMap(mapAndFlatten(Method_Ref_2))
    .forEach(Perform_Some_Operations);

虚拟示例:a link to Online Demo

多图()

另一种选择是使用 Java 16 mapMulti()。这个操作需要一个BiConsumer 类型的参数,它反过来消耗一个初始类型的元素和结果类型的消费者。提供给结果类型消费者的每个元素都将替换初始元素。

public static <T, R> BiConsumer<T, Consumer<R>> mapAndFlatten(Function<T, List<R>> mapper) {
    
    return (t, consumer) -> {
        List<R> list = mapper.apply(t);
        if (list != null) list.forEach(consumer);
    };
}

流可能如下所示:

Stream.of(t1, t2, t3)
    .<R1>mapMulti(mapAndFlatten(Method_Ref_1))
    .<R1>mapMulti(mapAndFlatten(Method_Ref_2))
    .forEach(Perform_Some_Operations);

虚拟示例:a link to Online Demo

笔记

  • 当您需要返回一个集合时,最好返回一个空集合而不是null。您可能会在许多权威资料中找到这个建议,例如 Joshua Bloch 的“Effective Java”。它使与结果值的交互更简单。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-02
    • 1970-01-01
    • 2011-05-25
    • 2021-03-11
    • 1970-01-01
    • 2023-03-13
    • 2019-03-30
    • 1970-01-01
    相关资源
    最近更新 更多