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?
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.
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.
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.
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>>
.
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());
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