Say I have a Map<? extends Object, List<String>>
I can get the values of the map easily enough, and iterate over it to produce a single List<String>
.
for (List<String> list : someMap.values()) { someList.addAll(list); }
Is there a way to flatten it in one shot?
List<String> someList = SomeMap.values().flatten();
To flatten a collection means to place them into a single object.
Stream flatMap() method is used to flatten a Stream of collections to a stream of objects. The objects are combined from all the collections in the original stream. The flatMap() method is a one-to-many transformation to the elements of the stream and then flattening the resulting elements into a new stream.
It means that in each iteration of each element the map() method creates a separate new stream. By using the flattening mechanism, it merges all streams into a single resultant stream. In short, it is used to convert a Stream of Stream into a list of values.
Using Java 8 and if you prefer not to instantiate a List
instance by yourself, like in the suggested (and accepted) solution
someMap.values().forEach(someList::addAll);
You could do it all by streaming with this statement:
List<String> someList = map.values().stream().flatMap(c -> c.stream()).collect(Collectors.toList());
By the way it should be interesting to know, that on Java 8 the accepted version seems to be indeed the fastest. It has about the same timing as a
for (List<String> item : someMap.values()) ...
and is a way faster than the pure streaming solution. Here is my little testcode. I explicitly don't name it benchmark to avoid the resulting discussion of benchmark flaws. ;) I do every test twice to hopefully get a full compiled version.
Map<String, List<String>> map = new HashMap<>(); long millis; map.put("test", Arrays.asList("1", "2", "3", "4")); map.put("test2", Arrays.asList("10", "20", "30", "40")); map.put("test3", Arrays.asList("100", "200", "300", "400")); int maxcounter = 1000000; System.out.println("1 stream flatmap"); millis = System.currentTimeMillis(); for (int i = 0; i < maxcounter; i++) { List<String> someList = map.values().stream().flatMap(c -> c.stream()).collect(Collectors.toList()); } System.out.println(System.currentTimeMillis() - millis); System.out.println("1 parallel stream flatmap"); millis = System.currentTimeMillis(); for (int i = 0; i < maxcounter; i++) { List<String> someList = map.values().parallelStream().flatMap(c -> c.stream()).collect(Collectors.toList()); } System.out.println(System.currentTimeMillis() - millis); System.out.println("1 foreach"); millis = System.currentTimeMillis(); for (int i = 0; i < maxcounter; i++) { List<String> mylist = new ArrayList<String>(); map.values().forEach(mylist::addAll); } System.out.println(System.currentTimeMillis() - millis); System.out.println("1 for"); millis = System.currentTimeMillis(); for (int i = 0; i < maxcounter; i++) { List<String> mylist = new ArrayList<String>(); for (List<String> item : map.values()) { mylist.addAll(item); } } System.out.println(System.currentTimeMillis() - millis); System.out.println("2 stream flatmap"); millis = System.currentTimeMillis(); for (int i = 0; i < maxcounter; i++) { List<String> someList = map.values().stream().flatMap(c -> c.stream()).collect(Collectors.toList()); } System.out.println(System.currentTimeMillis() - millis); System.out.println("2 parallel stream flatmap"); millis = System.currentTimeMillis(); for (int i = 0; i < maxcounter; i++) { List<String> someList = map.values().parallelStream().flatMap(c -> c.stream()).collect(Collectors.toList()); } System.out.println(System.currentTimeMillis() - millis); System.out.println("2 foreach"); millis = System.currentTimeMillis(); for (int i = 0; i < maxcounter; i++) { List<String> mylist = new ArrayList<String>(); map.values().forEach(mylist::addAll); } System.out.println(System.currentTimeMillis() - millis); System.out.println("2 for"); millis = System.currentTimeMillis(); for (int i = 0; i < maxcounter; i++) { List<String> mylist = new ArrayList<String>(); for (List<String> item : map.values()) { mylist.addAll(item); } } System.out.println(System.currentTimeMillis() - millis);
And here are the results:
1 stream flatmap 468 1 parallel stream flatmap 1529 1 foreach 140 1 for 172 2 stream flatmap 296 2 parallel stream flatmap 1482 2 foreach 156 2 for 141
Edit 2016-05-24 (two years after):
Running the same test using an actual Java 8 version (U92) on the same machine:
1 stream flatmap 313 1 parallel stream flatmap 3257 1 foreach 109 1 for 141 2 stream flatmap 219 2 parallel stream flatmap 3830 2 foreach 125 2 for 140
It seems that there is a speedup for sequential processing of streams and an even larger overhead for parallel streams.
Edit 2018-10-18 (four years after):
Using now Java 10 version (10.0.2) on the same machine:
1 stream flatmap 393 1 parallel stream flatmap 3683 1 foreach 157 1 for 175 2 stream flatmap 243 2 parallel stream flatmap 5945 2 foreach 128 2 for 187
The overhead for parallel streaming seems to be larger.
Edit 2020-05-22 (six years after):
Using now Java 14 version (14.0.0.36) on a different machine:
1 stream flatmap 299 1 parallel stream flatmap 3209 1 foreach 202 1 for 170 2 stream flatmap 178 2 parallel stream flatmap 3270 2 foreach 138 2 for 167
It should really be noted, that this was done on a different machine (but I think comparable). The parallel streaming overhead seems to be considerably smaller than before.
If you are using Java 8, you could do something like this:
someMap.values().forEach(someList::addAll);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With