Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 Streams FlatMap method example

I have been checking the upcoming Java update, namely: Java 8 or JDK 8. Yes, I am impatient, there's a lot of new stuff, but, there is something I don't understand, some simple code:

final Stream<Integer>stream = Stream.of(1,2,3,4,5,6,7,8,9,10); stream.flatMap(); 

the javadocs are

public <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)

Returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element. Each mapped stream is closed after its contents have been placed into this stream. (If a mapped stream is null an empty stream is used, instead.) This is an intermediate operation.

I would appreciate if somebody created some simple real-life examples about flatMap, how you could code it in previous java versions Java[6,7] and how you can code the same routines using Java 8.

like image 771
chiperortiz Avatar asked Mar 13 '14 14:03

chiperortiz


2 Answers

It doesn't make sense to flatMap a Stream that's already flat, like the Stream<Integer> you've shown in your question.

However, if you had a Stream<List<Integer>> then it would make sense and you could do this:

Stream<List<Integer>> integerListStream = Stream.of(     Arrays.asList(1, 2),      Arrays.asList(3, 4),      Arrays.asList(5) );  Stream<Integer> integerStream = integerListStream .flatMap(Collection::stream); integerStream.forEach(System.out::println); 

Which would print:

1 2 3 4 5 

To do this pre-Java 8 you just need a loops:

List<List<Integer>> integerLists = Arrays.asList(     Arrays.asList(1, 2),      Arrays.asList(3, 4),      Arrays.asList(5) )  List<Integer> flattened = new ArrayList<>();  for (List<Integer> integerList : integerLists) {     flattened.addAll(integerList); }  for (Integer i : flattened) {     System.out.println(i); } 
like image 155
Nick Holt Avatar answered Oct 13 '22 20:10

Nick Holt


Made up example

Imagine that you want to create the following sequence: 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 etc. (in other words: 1x1, 2x2, 3x3 etc.)

With flatMap it could look like:

IntStream sequence = IntStream.rangeClosed(1, 4)                           .flatMap(i -> IntStream.iterate(i, identity()).limit(i)); sequence.forEach(System.out::println); 

where:

  • IntStream.rangeClosed(1, 4) creates a stream of int from 1 to 4, inclusive
  • IntStream.iterate(i, identity()).limit(i) creates a stream of length i of int i - so applied to i = 4 it creates a stream: 4, 4, 4, 4
  • flatMap "flattens" the stream and "concatenates" it to the original stream

With Java < 8 you would need two nested loops:

List<Integer> list = new ArrayList<>(); for (int i = 1; i <= 4; i++) {     for (int j = 0; j < i; j++) {         list.add(i);     } } 

Real world example

Let's say I have a List<TimeSeries> where each TimeSeries is essentially a Map<LocalDate, Double>. I want to get a list of all dates for which at least one of the time series has a value. flatMap to the rescue:

list.stream().parallel()     .flatMap(ts -> ts.dates().stream()) // for each TS, stream dates and flatmap     .distinct()                         // remove duplicates     .sorted()                           // sort ascending     .collect(toList()); 

Not only is it readable, but if you suddenly need to process 100k elements, simply adding parallel() will improve performance without you writing any concurrent code.

like image 37
assylias Avatar answered Oct 13 '22 20:10

assylias