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

时间:2014-09-17 00:12:31

标签: java java-8 java-stream

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

我们说列表名称是结果。

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;
}

现在问题是尝试将结果传递到流中以使用流继续使用新值更新列表。根据java教程,尽管效率低下,但它是可能的。

  

&#34;如果您的reduce操作涉及向集合中添加元素,那么每次您的累加器函数处理元素时,它都会创建一个包含该元素的新集合,这是低效的。&#34;

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

<U> U reduce(U identity,
         BiFunction<U,? super T,U> accumulator,
         BinaryOperator<U> combiner)

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

2 个答案:

答案 0 :(得分:11)

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

使用流实现递归关系的方法是使用流工厂方法Stream.generateStream.iterate之一。 iterate工厂似乎提出了最明显的方法。对于递归函数的每个应用程序需要保留的状态在您的示例中需要两个整数,所以不幸的是我们必须创建一个对象来为我们保存这些:

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]

另外,您可以使用相同的技术生成Fibonacci序列。您所做的就是提供不同的起始值和迭代函数:

Stream.iterate(new IntPair(0, 1), p -> new IntPair(p.b, p.a + p.b))

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

答案 1 :(得分:3)

为了完整起见,这是一个不需要额外课程的解决方案。

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());

这是一种功能性方法,不需要中间值存储。但是,由于Java不是函数式编程语言,并且没有对这种递归函数定义进行优化,因此不建议将其用于较大的流。

因为对于这个例子,无论如何更大的流将以数字方式溢出并且计算是便宜的,这种方法是有效的。但对于其他用例,在使用普通Java解决此类问题时,您肯定会更喜欢存储对象(如Stuart Marks’ answer中所述)