Passing a collection using a reduce (3 parameters) function - streams java 8

前端 未结 2 428
离开以前
离开以前 2021-01-05 17:25

I am trying to calculate the multiplication of a value using the previous two values using java 8\'s stream. I want to call a function that will return an array/list/collect

相关标签:
2条回答
  • 2021-01-05 17:55

    Just for completeness, here is a solution which does not need an additional class.

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

    This is a functional approach which doesn’t need an intermediate value storage. However, since Java is not a functional programming language and doesn’t have optimizations for such a recursive function definition, this is not recommended for larger streams.

    Since for this example a larger stream would overflow numerically anyway and the calculation is cheap, this approach works. But for other use cases you will surely prefer a storage object when solving such a problem with plain Java (as in Stuart Marks’ answer)

    0 讨论(0)
  • 2021-01-05 18:16

    It looks like you're trying to implement a recurrence relation. The reduce method applies some function to a bunch of pre-existing values in the stream. You can't use reduce and take an intermediate result from the reducer function and "feed it back" into the stream, which is what you need to do in order to implement a recurrence relation.

    The way to implement a recurrence relation using streams is to use one of the streams factory methods Stream.generate or Stream.iterate. The iterate factory seems to suggest the most obvious approach. The state that needs to be kept for each application of the recurrence function requires two ints in your example, so unfortunately we have to create an object to hold these for us:

    static class IntPair {
        final int a, b;
        IntPair(int a_, int b_) {
            a = a_; b = b_;
        }
    }
    

    Using this state object you can create a stream that implements the recurrence that you want:

    Stream.iterate(new IntPair(1, 2), p -> new IntPair(p.b, p.a * p.b))
    

    Once you have such a stream, it's a simple matter to collect the values into a list:

    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]
    

    As an aside, you can use the same technique to generate the Fibonacci sequence. All you do is provide a different starting value and iteration function:

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

    You could also implement a similar recurrence relation using Stream.generate. This will also require a helper class. The helper class implements Supplier of the result value but it also needs to maintain state. It thus needs to be mutable, which is kind of gross in my book. The iteration function also needs to be baked into the generator object. This makes it less flexible than the IntPair object, which can be used for creating arbitrary recurrences.

    0 讨论(0)
提交回复
热议问题