Question

Suppose I have a List<String> and a List<Transfomer>. I want to apply each transformer to each string in the list.

Using Java 8 lambdas, I can do this:

strings.stream().map(s -> {
    for(Transformer t : transformers) {
        s = t.apply(s);
    }
    return s;
}).forEach(System.out::println);

But I'd like to do something more like this, however it results in a compile time error:

strings.stream().map(s -> transformers.stream().forEach(t -> s = t.apply(s))).forEach(System.out::println);

I'm just starting to play with lambdas, so maybe I just don't have the syntax correctly.

Was it helpful?

Solution

The best way to do this with streams is to use reduce:

// make a transformer that combines all of them as one
Transformer combinedTransformer =

    // the stream of transformers
    transformers.stream()

    // combine all the transformers into one
    .reduce(

        // apply each of the transformers in turn
        (t1, t2) -> x -> t2.apply(t1.apply(x)))

    );



// the stream of strings
strings.stream()

// transform each string with the combined transformer
.map(combinedTranformer::apply);

Of course, this assumes that transformers is non-empty; if there is a possibility that it is empty, than it is simple enough to use the two-argument overload of reduce instead, like so (this assumes Tranformer is a functional interface):

// make a transformer that combines all of them as one
Transformer combinedTransformer =

    // the stream of transformers
    transformers.stream()

    // combine all the transformers into one
    .reduce(

        // the no-op transformer
        x -> x,

        // apply each of the transformers in turn
        (t1, t2) -> x -> t2.apply(t1.apply(x)))

    );



// the stream of strings
strings.stream()

// transform each string with the combined transformer
.map(combinedTranformer::apply);

The reason you got a compiler error is that, as the error says, outside variables used in a lambda expression must be effectively final; that is, declaring them final (if they aren't already) must not change the meaning of the program, or change whether or not it compiles. Using a mutable assignment in a lambda is therefore generally forbidden, and with good reason: mutation screws up parallelization, and one of the major reasons lambdas were included in Java 8 was to allow easier parallel programming.

Generally speaking, whenever you want to "sum up" results in some way, reduce (in any of its three overloads) is your go-to method. Learning how to use map, filter, reduce, and flatMap effectively is very important when working with Streams.

OTHER TIPS

Lambdas (just like local classes) cannot ever assign to captured local variables, whether from an outer lambda, or from an enclosing method. Captured local variables must be effectively final.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top