Dark theme | Light theme

June 11, 2020

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:


import java.math.BigInteger;
import java.util.List;
import java.util.function.UnaryOperator;

import static;

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.