Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this usage of Stream::flatMap wrong?

Tags:

I expected to be able to use Stream::flatMap like this

public static List<String> duplicate(String s) {      List<String> l = new ArrayList<String>();     l.add(s);     l.add(s);      return l; }   listOfStrings.stream().flatMap(str -> duplicate(str)).collect(Collectors.toList()); 

But I get the following compiler error

Test.java:25: error: incompatible types: cannot infer type-variable(s) R listOfStrings.stream().flatMap(str -> duplicate(str)).collect(Collectors.toList());

(argument mismatch; bad return type in lambda expression List cannot be converted to Stream)
where R,T are type-variables: R extends Object declared in method flatMap(Function>) T extends Object declared in interface Stream

In scala I can do what I believe to be equivalent

scala> List(1,2,3).flatMap(duplicate(_)) res0: List[Int] = List(1, 1, 2, 2, 3, 3) 

Why is this not a valid usage of flatMap in java?

like image 760
Simon Avatar asked Sep 11 '15 08:09

Simon


People also ask

What is flatMap () method does in stream?

flatMap() V/s map() : It applies a function on each element of Stream and store return value into new Stream. It does not flatten the stream. But flatMap() is the combination of a map and a flat operation i.e, it applies a function to elements as well as flatten them.

What is the difference between map and flatMap on a stream?

The map() method wraps the underlying sequence in a Stream instance, whereas the flatMap() method allows avoiding nested Stream<Stream<R>> structure. Here, map() produces a Stream consisting of the results of applying the toUpperCase() method to the elements of the input Stream: List<String> myList = Stream.

What is difference between map () and flatMap () and reduce () method in Java stream?

Both map and flatMap can be applied to a Stream<T> and they both return a Stream<R> . The difference is that the map operation produces one output value for each input value, whereas the flatMap operation produces an arbitrary number (zero or more) values for each input value.


2 Answers

The lambda expression in flatMap needs to return a Stream, as can be seen by the argument of flatMap which is of type Function<? super T, ? extends Stream<? extends R>>.

The following code will compile and run fine:

listOfStrings.stream()              .flatMap(str -> duplicate(str).stream()) // note the .stream() here              .collect(Collectors.toList()); 

because the lambda expression str -> duplicate(str).stream() is of type Function<String, Stream<String>>.

like image 84
Tunaki Avatar answered Sep 28 '22 12:09

Tunaki


If you want to duplicate each object in the stream several times, you don't need to waste memory on this with additional ArrayList. There are several shorter and faster alternatives.

  • Generate new stream using Stream.generate, then limit it:

    listOfStrings.stream()              .flatMap(str -> Stream.generate(() -> str).limit(2))              .collect(Collectors.toList()); 
  • Generate sequence of numbers via IntStream.range and map them to the same string:

    listOfStrings.stream()              .flatMap(str -> IntStream.range(0, 2).mapToObj(i -> str))              .collect(Collectors.toList()); 
  • Use good old Collections.nCopies:

    listOfStrings.stream()              .flatMap(str -> Collections.nCopies(2, str).stream())              .collect(Collectors.toList()); 

If you are sure that you will always duplicate exactly two times, there's the shortest alternative:

listOfStrings.stream()              .flatMap(str -> Stream.of(str, str))              .collect(Collectors.toList()); 
like image 38
Tagir Valeev Avatar answered Sep 28 '22 11:09

Tagir Valeev