使用stream编写函数数组

xtr*_*tra 3 java function java-8 java-stream

我想为一个函数组合一个函数数组.给定多个函数,该方法应返回一个函数,该函数是输入函数的组合.

一种方法是

public static <T> Function<T,T> composeAll(Function<T,T>... functions){
    Function<T,T> res = Function.identity();
    for(Function<T,T> f : functions){
       res = res.compose(f);
    }
    return res;
}
Run Code Online (Sandbox Code Playgroud)

我希望通过首先创建函数数组的流来实现相同的结果.但我无法弄清楚如何做到这一点

public static <T> Function<T,T> composeAll2 (Function<T,T>... functions){
    Function<T,T> res = Function.identity();
    Arrays.stream(functions). ??? 
    return res;
}
Run Code Online (Sandbox Code Playgroud)

我现在应该在最后一个提出问号的方法中输入什么代码?

Lin*_*ica 6

您可以使用该Stream#reduce()操作:

public static <T> Function<T,T> composeAll2 (Function<T,T>... functions){
    return Arrays.stream(functions).reduce(Function.identity(), Function::compose);
}
Run Code Online (Sandbox Code Playgroud)

这与您组成函数的迭代方式完全相同.

  • `reduce(Function :: compose).orElse(Function.identity())`可能会产生稍微高效的函数(特别是当数组相当小时,内存方式),但它们在语义上是等价的. (2认同)
  • 鉴于当前实现`Function.identity()`总是返回通过lambda表达式生成的相同实例,没有什么,像`Function :: identity`这样的另一个方法引用可以在这里改进,因为它不会调用`identity( )`,但创建一个表示`Function :: identity`的实例."orElseGet"的优势只有在推迟真正昂贵的操作时才能实现. (2认同)

Zhe*_*lov 5

虽然@Lino 的回答很简洁,但它不安全且容易发生堆栈溢出:

List<Function<Integer, Integer>> list = Collections.nCopies(10000, i -> i + 1);
System.out.println(composeAll2(list.toArray(new Function[0])).apply(0));
Run Code Online (Sandbox Code Playgroud)

如果您运行此代码,它将失败并显示 StackOverflowError:

Exception in thread "main" java.lang.StackOverflowError
    at java.util.function.Function.lambda$compose$0(Function.java:68)
    at java.util.function.Function.lambda$compose$0(Function.java:68)
    at java.util.function.Function.lambda$compose$0(Function.java:68)
    at java.util.function.Function.lambda$compose$0(Function.java:68)
    at java.util.function.Function.lambda$compose$0(Function.java:68)
    at java.util.function.Function.lambda$compose$0(Function.java:68)
    ...
Run Code Online (Sandbox Code Playgroud)

为了避免这种情况,我建议根本不使用流并使用旧for-each循环:

public static <T> Function<T,T> composeAll2(Function<T,T>... functions){
    return input -> {
        T res = input;
        for (Function<T, T> f : functions) {
            res = f.apply(res);
        }
        return res;
    };
}
Run Code Online (Sandbox Code Playgroud)

这个版本composeAll2将使用一个恒定的堆栈空间。

  • 注意 `compose` 和 `andThen` 之间的区别。 (2认同)