使用reduce(3个参数)函数传递集合-streams java 8

使用reduce(3个参数)函数传递集合-streams java 8,java,java-8,java-stream,Java,Java 8,Java Stream,我试图使用Java8的流计算前两个值的乘法。我想调用一个返回数组/列表/集合的函数。我正在创建一个列表并向其中添加1,2 假设列表名是result public static void main (String[] args) { List<Integer> result = new ArrayList<Integer>(); result.add(1); result.add(2); int n = 5; //n can be anything, choosing 5

我试图使用Java8的流计算前两个值的乘法。我想调用一个返回数组/列表/集合的函数。我正在创建一个列表并向其中添加1,2

假设列表名是result

public static void main (String[] args) { 
List<Integer> result = new ArrayList<Integer>();
result.add(1);
result.add(2);
int n = 5; //n can be anything, choosing 5 for this example
res(n, result);
//print result which should be [1, 2, 2, 4, 8]
}
public static List<Integer> res(int n, List<Integer> result ) {
           result.stream()
                 .limit(n)
                 .reduce(identity, (base,index) -> base);

       //return result;
}
publicstaticvoidmain(字符串[]args){
列表结果=新建ArrayList();
结果.增加(1);
结果:增加(2);
int n=5;//n可以是任何东西,在本例中选择5
res(n,结果);
//打印结果,结果应为[1,2,2,4,8]
}
公共静态列表资源(int n,列表结果){
result.stream()
.限制(n)
.reduce(identity,(base,index)->base);
//返回结果;
}
现在的问题是尝试将结果传递到流中,以使用流使用新值不断更新列表。根据java教程,这是可能的,尽管效率很低

如果reduce操作涉及将元素添加到集合中,则每次累加器函数处理元素时,都会创建一个包含该元素的新集合,这是低效的

我是否需要使用可选的第三个参数BinaryOperator combiner来组合列表+结果

<U> U reduce(U identity,
         BiFunction<U,? super T,U> accumulator,
         BinaryOperator<U> combiner)
U减少(U标识,
双功能累加器,
二进制运算符组合器)

简言之;我想传递一个包含两个值的列表,让函数查找前两个值(1,2)的乘法,将其添加到列表中,然后查找最后两个值(2,2)的乘法,并将其添加到列表中,直到流达到极限。

看起来您正在尝试实现一个递归关系。
reduce
方法将一些函数应用于流中一组预先存在的值。您不能使用
reduce
并从reducer函数中获取中间结果并将其“反馈”到流中,这是实现递归关系所需的操作

使用streams实现递归关系的方法是使用streams工厂方法之一
Stream.generate
Stream.iterate
iterate
工厂似乎提出了最明显的方法。在您的示例中,需要为递归函数的每个应用程序保留的状态需要两个int,因此很遗憾,我们必须创建一个对象来保存这些int:

static class IntPair {
    final int a, b;
    IntPair(int a_, int b_) {
        a = a_; b = b_;
    }
}
使用此状态对象,可以创建实现所需重复的流:

Stream.iterate(new IntPair(1, 2), p -> new IntPair(p.b, p.a * p.b))
一旦有了这样一个流,将值收集到列表中就很简单了:

List<Integer> output =
    Stream.iterate(new IntPair(1, 2), p -> new IntPair(p.b, p.a * p.b))
          .limit(5)
          .map(pair -> pair.a)
          .collect(Collectors.toList());
System.out.println(output);

[1, 2, 2, 4, 8]

您还可以使用
Stream.generate
实现类似的重复关系。这也需要一个助手类。helper类实现结果值的
Supplier
,但它还需要维护状态。因此它需要是可变的,这在我的书中有点恶心。迭代函数也需要烘焙到生成器对象中。这使得它不如
IntPair
对象灵活,后者可用于创建任意重现。

为了完整起见,这里有一个不需要额外类的解决方案

List<Integer> output = Stream.iterate(
    (ToIntFunction<IntBinaryOperator>)f -> f.applyAsInt(1, 2),
    prev -> f -> prev.applyAsInt((a, b) -> f.applyAsInt(b, a*b) )
)
.limit(9).map(pair -> pair.applyAsInt((a, b)->a))
.collect(Collectors.toList());
List output=Stream.iterate(
(ToIntFunction)f->f.applyAsInt(1,2),
prev->f->prev.applyAsInt((a,b)->f.applyAsInt(b,a*b))
)
.limit(9).map(对->对.applyasit((a,b)->a))
.collect(Collectors.toList());
这是一种功能性方法,不需要中间值存储。但是,由于Java不是函数式编程语言,并且没有针对这种递归函数定义的优化,因此不建议对较大的流使用这种方法


因为在这个例子中,更大的流无论如何都会在数值上溢出,而且计算成本很低,所以这种方法是有效的。但是对于其他用例,当使用纯Java解决此类问题时,您肯定会更喜欢存储对象(如中)

考虑到
reduce
可能不是实现此功能的正确工具这一事实,这是一个不错的解决方案。我在想,与其创建另一个类,也许我们可以使用streamconcat并重用流的元素?
List<Integer> output = Stream.iterate(
    (ToIntFunction<IntBinaryOperator>)f -> f.applyAsInt(1, 2),
    prev -> f -> prev.applyAsInt((a, b) -> f.applyAsInt(b, a*b) )
)
.limit(9).map(pair -> pair.applyAsInt((a, b)->a))
.collect(Collectors.toList());