Java: is there more elegant way to extract a new list from an existing list using reduce?

410 Views Asked by At

I'm trying to take a list of elements, make some manipulation on part of these elements and put the output of these manipulations in a new list. I want to do it with only one iteration on the list.

The only way I found to do it is:

List<Integer> newList = numList.stream().reduce(new ArrayList<Integer>(),
            (acc, value) -> {
                if (value % 2 == 0) {
                    acc.add(value * 10);
                }
                return acc;
            },
            (l1, l2) -> {
                l1.addAll(l2);
                return l1;
            }
        );

As you can see, it's very cumbersome.

I can of course use filter and then map, but in this case I iterate the list twice.

In other languages (Javascript for example) this kind of reduce operation is very straightforward, for example:

arr.reduce((acc, value) => { 
    if (value % 2 == 0) {
        acc.push(value * 10);
    }
    return acc;
}, new Array())

Amazing! I was thinking to myself whether Java has some nicer version for this reduce or the Java code I wrote is the shortest way to do such an operation.

2

There are 2 best solutions below

4
Eran On

I can of course use filter and they map, but in this case I iterate the list twice.

That's not true. The elements of the Stream are iterated once, no matter how many intermediate operations you have.

Besides, the correct way to perform mutable reduction is to use collect:

List<Integer> newList =
    numList.stream()
            .filter(v -> v % 2 == 0)
            .map(v -> v * 10)
            .collect(Collectors.toList());
4
Andronicus On

What you're doing is the opposite of functional programming, because you're using functions only to get a side effect.

You can simply not use reduce to make it even more explicit:

List<Integer> newList = numList.stream()
    .filter(value -> value % 2 == 0)
    .map(value -> value * 10)
    .collect(Collectors.toList());

Edit: Streams iterate over the collection once by definition. According to the javadoc:

A stream should be operated on (invoking an intermediate or terminal stream operation) only once.