Java Joy: Reapply Function With Stream iterate
In Java we can use the iterate
method of the Stream
class to create an unbounded stream based on function invocations. We pass to the iterate
method an initial value and a function that can be applied to the value. The first element in the unbounded stream is the initial value, the next element is the result of the function invocation with as argument the value from the previous element and this continues for each new element. Suppose we have a function expressed as lambda expression i → i + 2
. When we use this lambda expression with the iterate
method and a initial value of 1
we get a stream of 1
, 1 → 1 + 2
, 3 → 3 + 2
, ….
As we get an unbounded stream we must for example use limit
to get the values we want from the stream. But we can also use an extra argument for the iterate
method that is a Predicate
definition. The iterate
method will provide elements as long as the result of the Predicate
is true
. This way we the result of the iterate
method is a bounded stream.
In the following Java example we use the iterate
method with different arguments and lambda expressions:
package mrhaki.stream;
import java.math.BigInteger;
import java.util.List;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toUnmodifiableList;
public class Iterate {
public static void main(String[] args) {
// Create unbounded stream with odd numbers.
var odds = Stream.iterate(1, i -> i + 2);
// We use limit(5) to get the first 10 odd numbers from the unbounded stream.
assert odds.limit(5).collect(toUnmodifiableList()).equals(List.of(1, 3, 5, 7, 9));
// Create stream with even numbers, but here we use a predicate as
// second argument to determine that we stop after value 10.
var evens = Stream.iterate(0, i -> i <= 10, i -> i + 2);
assert evens.collect(toUnmodifiableList()).equals(List.of(0, 2, 4, 6, 8, 10));
// Define infinite stream with growing string.
// The first element is ar, next argh, then arghgh etc.
var pirate = Stream.iterate("ar", s -> s + "gh");
// We get the 5-th element for a crumpy pirate.
var crumpyPirate = pirate.skip(4).findFirst().get();
assert crumpyPirate.equals("arghghghgh");
// Function that returns the given amount
// plus interest of 1.25%.
UnaryOperator<Double> cumulativeInterest = amount -> amount + (amount * 0.0125);
// Lazy sequence where each entry is the
// cumulative amount with interest based
// on the previous entry.
// We start our savings at 500.
var savings = Stream.iterate(500d, cumulativeInterest);
// First element is start value, so we skip first five elements
// to get value after 5 years.
assert savings.skip(5).findFirst().get() == 532.0410768127441;
// Define infinite unbounded stream
// where each element is the doubled value of the previous element.
var wheatChessboard = Stream.iterate(BigInteger.valueOf(1), value -> value.add(value));
// Sum of all values for all chessboard squares is an impressive number.
var square64 = wheatChessboard.limit(64).reduce(BigInteger::add).get();
assert square64.equals(new BigInteger("18446744073709551615"));
}
}
Written with Java 14.