Don't worry about any performance differences, they're going to be minimal in this case normally.

Method 2 is preferable because

  1. it doesn't require mutating a collection that exists outside the lambda expression.

  2. it's more readable because the different steps that are performed in the collection pipeline are written sequentially: first a filter operation, then a map operation, then collecting the result (for more info on the benefits of collection pipelines, see Martin Fowler's excellent article.)

  3. you can easily change the way values are collected by replacing the Collector that is used. In some cases you may need to write your own Collector, but then the benefit is that you can easily reuse that.

Answer from Herman Bovens on Stack Overflow
Top answer
1 of 8
227

Don't worry about any performance differences, they're going to be minimal in this case normally.

Method 2 is preferable because

  1. it doesn't require mutating a collection that exists outside the lambda expression.

  2. it's more readable because the different steps that are performed in the collection pipeline are written sequentially: first a filter operation, then a map operation, then collecting the result (for more info on the benefits of collection pipelines, see Martin Fowler's excellent article.)

  3. you can easily change the way values are collected by replacing the Collector that is used. In some cases you may need to write your own Collector, but then the benefit is that you can easily reuse that.

2 of 8
59

I agree with the existing answers that the second form is better because it does not have any side effects and is easier to parallelise (just use a parallel stream).

Performance wise, it appears they are equivalent until you start using parallel streams. In that case, map will perform really much better. See below the micro benchmark results:

Benchmark                         Mode  Samples    Score   Error  Units
SO28319064.forEach                avgt      100  187.310 ± 1.768  ms/op
SO28319064.map                    avgt      100  189.180 ± 1.692  ms/op
SO28319064.mapWithParallelStream  avgt      100   55,577 ± 0,782  ms/op

You can't boost the first example in the same manner because forEach is a terminal method - it returns void - so you are forced to use a stateful lambda. But that is really a bad idea if you are using parallel streams.

Finally note that your second snippet can be written in a sligthly more concise way with method references and static imports:

myFinalList = myListToParse.stream()
    .filter(Objects::nonNull)
    .map(this::doSomething)
    .collect(toList()); 
🌐
Reddit
reddit.com › r/java › why does the list interface have foreach, but not map?
r/java on Reddit: Why does the List interface have forEach, but not map?
October 26, 2024 -

Pretty much the title: It's kind of annoying and "bloaty" to create a Stream from the List, just to do a map (or filter) and then convert it back to List. Isn't it also inefficient?

Is there a solid, philosophical justification behind this choice, or it is like that mostly for backward compatibility?

And on a broader, more non-orthodox note, why isn't every List a Stream? I fully understand the other way round, i.e. obviously not every Stream is a List, but this way..

Thanks a lot

Top answer
1 of 19
163
why isn't every List a Stream? To answer this part of the question, it's because a stream is a fundamentally different datatype than a list. A list represents a collection of data, while a stream represents a pipeline for processing data. Streams can only be processed once, and are designed to perform multiple operations on some provided data. They don't store any intermediate results, and then do lazy processing once you specify a terminal operator (like toList() or forEach()). For example, if you had something like this: Stream a = List.of(1, 2, 3).stream(); Stream b = a.filter(n -> n > 1); Stream c = b.map(n -> String.valueOf(n)); List d = c.toList(); This is functionally identical to this: List d = List.of(1, 2, 3).stream() .filter(n -> n > 1) .map(n -> String.valueOf(n)) .toList(); If you inspected b, however, it would not contain any data about what elements were remaining, because the stream has not executed yet. Assigning the various steps of the stream along the way doesn't actually DO anything to the data, it just creates pointers to instances of the pipeline. As mentioned before, the pipeline can only be executed once, which means a stream is consumed when you perform a terminal operation on it. It also can't be executed if you chain it to another pipeline. If, in the example above, you tried to do b.toList(), you'd get an IllegalStateException, because b was already operated on. If you did c.toList(), you'd get a list of "2", "3". If you did c.toList() a second time, you'd once again get an IllegalStateExcecution, because c has already been consumed. The intermediate operators like map actually do nothing except modify the pipeline definition until you call toList at the end. Once you execute toList, then the pipeline operates in a lazy way, evaluating the data elements one at a time. So, to recap: List: collection of ordered data Stream: an operation pipeline definition
2 of 19
24
Because forEach is easy to fully specify, as it just returns void. map would need to return some List, but what implementation? Stream allows you to specify, but if List would, things would get complicated. And if List, why not Set, Collection, or Iterable (forEach is actually in Iterable)? And if it's in Iterable, the question of the implementation of the returned type becomes even more complex. But why just map? Follow this through, and repeat for filter and flatMap, and Iterable becomes Stream, only more complicated, because an Iterable can be consumed multile times.
🌐
Medium
medium.com › @kiarash.shamaii › list-map-vs-stream-map-in-java-44440284386
Medium
August 28, 2024 - List<String> list = Arrays.asList("a", "b", "c"); list.forEach(System.out::println); Functional Programming: The Stream API promotes a functional style of programming, allowing for operations like filtering, mapping, and reducing.
🌐
Oracle
docs.oracle.com › javase › 8 › docs › api › java › util › Map.html
Map (Java Platform SE 8 )
July 21, 2026 - The Map interface provides three collection views, which allow a map's contents to be viewed as a set of keys, collection of values, or set of key-value mappings. The order of a map is defined as the order in which the iterators on the map's collection views return their elements.
🌐
GeeksforGeeks
geeksforgeeks.org › java › difference-between-list-set-and-map-in-java
Difference between List, Set and Map in Java - GeeksforGeeks
June 6, 2026 - List stores elements in an ordered sequence and allows duplicate values. Set stores only unique elements and does not allow duplicates. Map stores data in the form of key-value pairs, where each key must be unique.
🌐
W3Schools
w3schools.com › java › java_map.asp
Java Map
Java Examples Java Videos Java ... Q&A ... The Map interface is a part of the Java Collections Framework and is used to store key-value pairs....
🌐
WPI Computer Science
web.cs.wpi.edu › ~cs2102 › b15 › Labs › map-and-filter.html
Map, Filter, Lambdas and Java Streams
To do this in Java, you need to know (a) how to call the filter method on a list, and (b) how to pass the longerThanFive method as an argument. Both involve slight changes to what you have seen before. Java doesn’t provide filter and map directly on lists.
Find elsewhere
🌐
Coderanch
coderanch.com › t › 680277 › java › Creating-map-maps-list-Java
Creating a map of maps from a list using Java 8 streams [Solved] (Java in General forum at Coderanch)
Java in General · Lambdas and ... I also have a list<MonitoredData> filled with such objects. The idea is to create a Map<Long, Map<String, Long>> using a stream from the list of MonitoredData....
🌐
Baeldung
baeldung.com › home › java › java collections › java map › how to convert list to map in java
How to Convert List to Map in Java | Baeldung
April 4, 2025 - Learn several ways to convert a List into a Map using Custom Suppliers. ... Learn how to convert a List to a String using different techniques. ... How to convert between a List and a Set using plain Java, Guava or Apache Commons Collections.
🌐
Coderanch
coderanch.com › t › 755986 › java › Create-Map-existing-List-Entry
Create a Map out of existing List of Entry (Java in General forum at Coderanch)
November 28, 2022 - Anyway, here is what I have: List<Map.Entry<String, String>> Eg: 'One', 'Sunday' 'One', 'Monday' 'One', 'Tuesday' 'Two', 'Wednesday' 'Two', 'Thursday' 'Three', 'Friday' And here is what I want: Map<String, List<String>> Eg: 'One' -> 'Sunday', 'Monday', 'Tuesday' 'Two' -> 'Wednesday', 'Thursday' 'Three' -> 'Friday' Hope the above helps
🌐
GeeksforGeeks
geeksforgeeks.org › java › conversion-of-java-maps-to-list
Conversion of Java Map to List - GeeksforGeeks
July 11, 2025 - From Java 8, we can use the Stream API to make the conversion process even more easy and functional. We can stream the map key or values and collect them into a list.
🌐
Coderanch
coderanch.com › t › 698071 › java › Converting-List-Map
Converting from List To Map? (Features new in Java 8 forum at Coderanch)
August 21, 2018 - Piet Souris wrote:hi Mike, for the toMap you need two mappers, one that maps the element to a key, and one that maps the element to a value (and if there is a possibility of having non unique keys, a BiFunction that tells what to do with the values in that case).
🌐
Mkyong
mkyong.com › home › java8 › java 8 streams map() examples
Java 8 Streams map() examples | mkyong.com
April 3, 2017 - package com.mkyong.java8; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class TestJava8 { public static void main(String[] args) { List<String> alpha = Arrays.asList("a", "b", "c", "d"); //Before Java8 List<String> alphaUpper = new ArrayList<>(); for (String s : alpha) { alphaUpper.add(s.toUpperCase()); } System.out.println(alpha); //[a, b, c, d] System.out.println(alphaUpper); //[A, B, C, D] // Java 8 List<String> collect = alpha.stream().map(String::toUpperCase).collect(Collectors.toList()); System.out.println(collect); //[A, B, C, D] // Extra, streams apply to any data type.
🌐
Stack Abuse
stackabuse.com › java-convert-map-to-list
Java 8: How to Convert a Map to List
October 10, 2023 - Java 8 introduced us to the Stream API - which were meant as a step towards integrating Functional Programming into Java to make laborious, bulky tasks more readable and simple. Streams work wonderfully with collections, and can aid us in converting a Map to a List.
🌐
Baeldung
baeldung.com › home › java › java collections › java map › converting hashmap values to an arraylist in java
Converting HashMap Values to an ArrayList in Java | Baeldung
June 9, 2025 - The Lists class comes with the newArrayList() method, which creates a new ArrayList based on the given elements. As shown above, we used a custom EntryTransformer to get the value from the key-value pair. Then, we passed our transformer to the Maps.transformEntries() method alongside the HashMap.
🌐
Baeldung
baeldung.com › home › java › java list › create list of object from another type using java
Create List of Object From Another Type Using Java | Baeldung
March 7, 2025 - As the code above shows, preparing an empty list for TennisPlayerCandidate objects is unnecessary. The filter().map() pipeline delivers a Stream of TennisPlayerCandidate instances.
Top answer
1 of 2
2

A stream is a sequence of values (possibly unordered / parallel). map() is what you use when you want to map a single value in the sequence to some single other value. Say, map "alturkovic" to "ALTURKOVIC". flatMap() is what you use when you want to map a single value in the sequence to 0, 1, or many other values. Hence why a flatMap lambda needs to turn a value into a stream of values. flatMap can thus be used to take, say, a list of lists of string, and turn that into a stream of just strings.

Here, you want to map a single entry from your map (a single key/value pair) into a single element (a string describing it). 1 value to 1 value. That means flatMap is not appropriate. You're looking for just map.

Furthermore, you need both key and value to perform your mapping op, so, keySet() is also not appropriate. You're looking for entrySet(), which gives you a set of all k/v pairs, juts what we need.

That gets us to:

map.entrySet().stream()
   .map(e -> String.format("%s-%s", e.getKey(), e.getValue()))
   .collect(Collectors.toList());

Your original code makes no effort to treat a single value from a map (which is a List<String>) as separate values; you just call .toString() on the entire ordeal, and be done with it. This means the produced string looks like, say, [Hello, World] given a map value of List.of("Hello", "World"). If you don't want this, you still don't want flatmap, because streams are also homogenous - the values in a stream are all of the same kind, and thus a stream of 'key1 value1 value2 key2 valueA valueB' is not what you'd want:

map.entrySet().stream()
   .map(e -> String.format("%s-%s", e.getKey(), myPrint(e.getValue())))
   .collect(Collectors.toList());

public static String myPrint(List<String> in) {
   // write your own algorithm here
}

Stream API just isn't the right tool to replace that myPrint method.

A third alternative is that you want to smear out the map; you want each string in a mapvalue's List<String> to first be matched with the key (so that's re-stating that key rather a lot), and then do something to that. NOW flatMap IS appropriate - you want a stream of k/v pairs first, and then do something to that, and each element is now of the same kind. You want to turn the map:

key1 = [value1, value2]
key2 = [value3, value4]

first into a stream:

key1:value1
key1:value2
key2:value3
key2:value4

and take it from there. This explodes a single k/v entry in your map into more than one, thus, flatmapping needed:

return map.entrySet().stream()
    .flatMap(e -> e.getValue().stream()
      .map(v -> String.format("%s-%s", e.getKey(), v))
    .collect(Collectors.toList());

Going inside-out, it maps a single entry within a list that belongs to a single k/v pair into the string Key-SingleItemFromItsList.

2 of 2
0

Adding my two cents to excellent answer by @rzwitserloot. Already flatmap and map is explained in his answer.

List<String> resultLists =  myMap.entrySet().stream()
        .flatMap(mapEntry -> printEntries(mapEntry.getKey(),mapEntry.getValue())).collect(Collectors.toList());
System.out.println(resultLists);

Splitting this to a separate method gives good readability IMO,

private static Stream<String> printEntries(String key, List<String> values) {
    return values.stream().map(val -> String.format("%s-%s",key,val));
  }
🌐
Marc Nuri
blog.marcnuri.com › home › java streams: convert list into map
Java Streams: Convert List into Map - Marc Nuri
January 27, 2026 - In this post, I'll cover everything you need to know about converting a List to a Map using Collectors.toMap(): ... If you're using a version before Java 16, you can use a traditional class with getters instead.