简体   繁体   中英

what does java8 stream map do here?

I was confused about the difference between map() and forEach() method in java8 stream. For instance,

List<String> strings = Lists.newArrayList("1", "2");
Map<String, String> map = Maps.newHashMap();
strings.stream().map(s->map.put(s, s));
System.out.println(map);

I got empty output here, but if I change map to forEach() just like

List<String> strings = Lists.newArrayList("1", "2");
Map<String, String> map = Maps.newHashMap();
strings.stream().forEach(s->map.put(s, s));
System.out.println(map);

I can get

{1=1, 2=2}

Why it just didn't run map() method? What's difference between them?

strings.stream().map(s->map.put(s, s));

does nothing, since the stream pipeline is not processed until you execute a terminal operation. Therefore the Map remains empty.

Adding a terminal operation to the stream pipeline will cause map.put(s, s) to be executed for each element of the Stream required by the terminal operation (some terminal operations require just one element, while others require all elements of the Stream ).

On the other hand, the second stream pipeline:

strings.stream().forEach(s->map.put(s, s));

ends with a terminal operation - forEach - which is executed for each element of the Stream .

That said, both snippets are misusing Stream s. In order to populate a Collection or a Map based on the contents of the Stream , you should use collect() , which can create a Map or a Collection and populate it however you like. forEach and map have different purposes.

For example, to create a Map :

List<String> strings = Lists.newArrayList("1", "2");
Map<String, String> map = strings.stream()
                                 .collect(Collectors.toMap(Function.identity(),
                                                           Function.identity()));
System.out.println(map);

The difference is this:

  • The idea of forEach() is to "work" on each element of the underlying collection (by having a side effect ) whereas
  • map() is about applying a method on each object and putting the result of that into a new stream

That is also the reason why your stream().map() doesn't result in something - because you throw away the new stream created by the map() call!

In that sense, the signatures of the two methods tell you that:

void forEach(BiConsumer<? super K,? super V> action)

Performs the given action for each entry in this map until all entries have been processed

versus

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

Returns a stream consisting of the results of applying the given function to the elements of this stream.

And for the record: only map() is a stream method - forEach() exists for both, streams and Collections/Iterables.

The documentation for each instance method of Stream states whether the method is an intermediate operation, or a terminal operation. Only terminal operations will cause the stream to be evaluated. Intermediate operations may not be performed unless required by a downstream terminal operation.

This can improve efficiency, especially when the terminal operation is a short-circuit operation, like findAny() . A pipeline with a complex set of intermediate operations doesn't need to be executed for additional elements after a matching element has been found.

In your case, forEach() is a terminal operation; it causes the pipeline to be executed. map() is an intermediate operation; it doesn't have to do anything unless required by a downstream terminal operation.

You should use

stream().collect(Collectors.toMap(s -> s,s -> s)

...or something along those lines, instead of stream().map(...)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM