7

I'm learning how to use Java streams and need some help understanding how to stream nested collections and collect the results back into a collection.

In the simple example below, I've created 2 ArrayLists and added them to an ArrayList. I want to be able to perform a simple function on each nested collection and then capture the results into a new collection. The last line of code doesn't even compile. Any explanations would be appreciated!

    ArrayList<Integer> list1 = new ArrayList<Integer>(Arrays.asList(1,2,3));
    ArrayList<Integer> list2 = new ArrayList<Integer>(Arrays.asList(4,5,6));

    ArrayList<ArrayList<Integer>> nested = new ArrayList<ArrayList<Integer>>();
    nested.add(list1);
    nested.add(list2);

    ArrayList<ArrayList<Integer>> result = nested.stream()
            .map(list -> list.add(100))
            .collect(Collectors.toList());
Steve Kim
  • 73
  • 4
  • What do you exactly want? Your `nested.stream()` will return a `Stream>` so you can operate each nested list as expected. Or do you need to flatten this into `Stream` so you can operate on each element of the nested list? – Luiggi Mendoza Nov 04 '17 at 17:42
  • Collectors.toList() is used to create a List. Not an ArrayList. Program to interfaces, not implementations. Also, you should not mutate the elements on which you're iterating with a stream. That's not what streams are for. – JB Nizet Nov 04 '17 at 17:48
  • `List`'s add method has return type `boolean` so by writing this `.map(list -> list.add(100))` will return `Stream` – Ajit Soman Nov 04 '17 at 17:58
  • @LuiggiMendoza I'm coming from JavaScript and doing something like this is pretty simple. All I want to be able to do is add an element to each nested list and then have a new collection where I can see that each nested list has the new element. – Steve Kim Nov 04 '17 at 18:08
  • Thanks for the explanations! Makes sense now! – Steve Kim Nov 04 '17 at 18:19
  • @SteveKim adding an element to a list, in Java or in JavaScript, doesn't create a new list. All you need is `nested.forEach(innerList -> innerList.add(100))` – JB Nizet Nov 04 '17 at 19:03
  • @JBNizet Actually, in JavaScript mapping over an array of arrays and adding or concatenating an element to each subarray would return a new array with each subarray having the added element. – Steve Kim Nov 06 '17 at 00:20

2 Answers2

2

The problem is that List#add doesn't return a List. Instead, you need to return the list after the mapping:

List<ArrayList<Integer>> result = nested.stream()
        .map(list -> {
            list.add(100);
            return list;
        })
        .collect(Collectors.toList());

Or you may skip using map and do it using forEach, since ArrayList is mutable:

nested.forEach(list -> list.add(100));
Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
0

You can use Stream.of() to do this, without creating the nested stricture beforehand.

        List<ArrayList<Integer>> nestedList = Stream.of(list1, list2)
            .map(list -> {list.add(100); return list;})
            .collect(Collectors.toList());

And maybe extract the map operation into a separate function.

mitra
  • 182
  • 11