Equivalent of Scala's foldLeft in Java 8

GA1 picture GA1 · Dec 20, 2016 · Viewed 21k times · Source

What is the equivalent of of Scala's great foldLeft in Java 8?

I was tempted to think it was reduce, but reduce has to return something of identical type to what it reduces on.

Example:

import java.util.List;

public class Foo {

    // this method works pretty well
    public int sum(List<Integer> numbers) {
        return numbers.stream()
                      .reduce(0, (acc, n) -> (acc + n));
    }

    // this method makes the file not compile
    public String concatenate(List<Character> chars) {
        return chars.stream()
                    .reduce(new StringBuilder(""), (acc, c) -> acc.append(c)).toString();
    }
}

The problem in the code above is the accumulator: new StringBuilder("")

Thus, could anyone point me to the proper equivalent of the foldLeft/fix my code?

Answer

dzs picture dzs · May 14, 2017

There is no equivalent of foldLeft in Java 8's Stream API. As others noted, reduce(identity, accumulator, combiner) comes close, but it's not equivalent with foldLeft because it requires the resulting type B to combine with itself and be associative (in other terms, be monoid-like), a property that not every type has.

There is also an enhancement request for this: add Stream.foldLeft() terminal operation

To see why reduce won't work, consider the following code, where you intend to execute a series of arithmetic operations starting with given number:

val arithOps = List(('+', 1), ('*', 4), ('-', 2), ('/', 5))
val fun: (Int, (Char, Int)) => Int = {
  case (x, ('+', y)) => x + y
  case (x, ('-', y)) => x - y
  case (x, ('*', y)) => x * y
  case (x, ('/', y)) => x / y
}
val number = 2
arithOps.foldLeft(number)(fun) // ((2 + 1) * 4 - 2) / 5

If you tried writing reduce(2, fun, combine), what combiner function could you pass that combines two numbers? Adding the two numbers together clearly does not solve it. Also, the value 2 is clearly not an identity element.

Note that no operation that requires a sequential execution can be expressed in terms of reduce. foldLeft is actually more generic than reduce: you can implement reduce with foldLeft but you cannot implement foldLeft with reduce.