Search code examples
javajava-8reducefoldleft

Equivalent of Scala's foldLeft in Java 8


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?


Solution

  • Update:

    Here is initial attempt to get your code fixed:

    public static String concatenate(List<Character> chars) {
            return chars
                    .stream()
                    .reduce(new StringBuilder(),
                                    StringBuilder::append,
                                    StringBuilder::append).toString();
        }
    

    It uses the following reduce method:

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

    It may sound confusing but if you look at the javadocs there is a nice explanation that may help you quickly grasp the details. The reduction is equivalent to the following code:

    U result = identity;
    for (T element : this stream)
         result = accumulator.apply(result, element)
    return result;
    

    For a more in-depth explanation please check this source.

    This usage is not correct though because it violates the contract of reduce which states that the accumulator should be an associative, non-interfering, stateless function for incorporating an additional element into a result. In other words since the identity is mutable the result will be broken in case of parallel execution.

    As pointed in the comments below a correct option is using the reduction as follows:

    return chars.stream().collect(
         StringBuilder::new, 
         StringBuilder::append, 
         StringBuilder::append).toString();
    

    The supplier StringBuilder::new will be used to create reusable containers which will be later combined.