0

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.

Andronicus
  • 25,419
  • 17
  • 47
  • 88
CrazySynthax
  • 13,662
  • 34
  • 99
  • 183

2 Answers2

7

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());
Eran
  • 387,369
  • 54
  • 702
  • 768
  • OP wants _using reduce_ – Hadi J Jun 10 '20 at 08:54
  • 5
    @HadiJ OP want a more elegant way. The more elegant (and correct) way is not to use reduce at all. – Eran Jun 10 '20 at 08:56
  • "The elements of the Stream are iterated once, no matter how many intermediate operations you have." - can you give me a link supporting that? – CrazySynthax Jun 10 '20 at 08:57
  • 6
    @CrazySynthax How about this : `The elements of a stream are only visited once during the life of a stream. Like an Iterator, a new stream must be generated to revisit the same elements of the source.` (source - https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html) – Eran Jun 10 '20 at 08:59
2

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.

Andronicus
  • 25,419
  • 17
  • 47
  • 88