13

This is a example: code A:

files.forEach(f -> {
    //TODO
});

and another code B may use on this way:

files.stream().forEach(f -> { });

What is the difference between both, with stream() and no stream()?

Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
ming
  • 313
  • 1
  • 5
  • 14
  • 2
    That's not really accurate. Both are semantically equivalent to that, but neither will necessarily have that implementation. – Louis Wasserman Mar 17 '15 at 03:38
  • code A is equals for(File f : files) – ming Mar 17 '15 at 03:43
  • code B should be used when there are some Intermediate operations (map, skip, concat, substream, distinct, filter, sorted, limit, peek..) else it is suboptimal. thanks guys! – ming Mar 17 '15 at 03:57

4 Answers4

26

Practically speaking, they are mostly the same, but there is a small semantic difference.

Code A is defined by Iterable.forEach, whereas code B is defined by Stream.forEach. The definition of Stream.forEach allows for the elements to be processed in any order -- even for sequential streams. (For parallel streams, Stream.forEach will very likely process elements out-of-order.)

Iterable.forEach gets an Iterator from the source and calls forEachRemaining() on it. As far as I can see, all current (JDK 8) implementations of Stream.forEach on the collections classes will create a Spliterator built from one of the source's Iterators, and will then call forEachRemaining on that Iterator -- just like Iterable.forEach does. So they do the same thing, though the streams version has some extra setup overhead.

However, in the future, it's possible that the streams implementation could change so that this is no longer the case.

(If you want to guarantee ordering of processing streams elements, use forEachOrdered() instead.)

Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
  • Upvoted for potential change of the implementation may break the current contract – Unihedron Mar 17 '15 at 07:57
  • 4
    Surprisingly, the `default` implementation of `Iterable.forEach` does not call `forEachRemaining` on the `Iterator`; it iterates using a for-each loop. The default `Stream.forEach` will call `forEachRemaining` on the `Spliterator` which will end up calling `Iterator.forEachRemaining` unless `Iterable.spliterator()` has been overridden (which most JRE provided `Collection`s do). So even for Collections where `collection.forEach(…)` and `collection.stream().forEach(…)` do basically the same, they end up in different code doing it. But for certain `Collection`s there is a significant difference. – Holger Mar 17 '15 at 21:35
  • 2
    A notable difference exist for `Vector` and the collections returned by the `Collections.synchronized…` methods as their `forEach` implementations will be `synchronized` while calling `stream().forEach(…)` requires manual synchronization. – Holger Mar 17 '15 at 21:38
2

There is no difference in terms of semantics, though the direct implementation without stream is probably slightly more efficient.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • 1
    Actually, there *are* differences in term of semantics, e.g. the ordering guarantees (or the absence of them in the `Stream.forEach` case). – Holger Mar 17 '15 at 21:11
-1

A stream is an sequence of elements (i.e a data structure) for using up an operation or iteration. Any Collection can be exposed as a stream. The operations you perform on a stream can either be

Intermediate operations (map, skip, concat, substream, distinct, filter, sorted, limit, peek..) producing another java.util.stream.Stream but the intermediate operations are lazy operations, which will be executed only after a terminal operation was executed.

And the Terminal operations (forEach, max, count, matchAny, findFirst, reduce, collect, sum, findAny ) producing an object that is not a stream.

Basically it is similar to pipeline as in Unix.

Swathi
  • 602
  • 4
  • 17
-3

Both approaches uses the terminal operation Iterable.forEach, but the version with .stream() also unnecessarily creates a Stream object representing the List. While there is no difference, it is suboptimal.

Unihedron
  • 10,902
  • 13
  • 62
  • 72
  • 5
    Sorry, this isn't correct. The code A is indeed `Iterable.forEach`, but code B is `Stream.forEach` which is not guaranteed to process the elements in any particular order, even for sequential streams. – Stuart Marks Mar 17 '15 at 04:39
  • @Stuart: That is an API detail, whereas StreamSupport uses underlying Iterable.forEach for streams of Iterable (arrays excluded) since JDK 8. Therefore, your concern of a potential for an execution difference is non-existent. Nothing is wrong with my answer. – Unihedron Mar 17 '15 at 07:54
  • The concern can't be non-existent ^^ The difference is [very real](http://stackoverflow.com/questions/28259636/is-this-a-bug-in-files-lines-or-am-i-misunderstanding-something-about-paralle), too. Btw: what makes `Iterable.forEach` terminal? – a better oliver Mar 17 '15 at 08:14
  • 1
    @Unihedro Very amusing that you claim "API detail" to justify your appeal to an implementation detail! Your statement is simply wrong; the `forEach` method in `Stream` is unrelated to the one in `Iterable`. The current implementation in the current version of the Oracle JDK happens to use the `Iterable` one, but this is mostly irrelevant, as you cannot count on this staying the same. You can only rely on the API specification. – Brian Goetz Mar 17 '15 at 15:27
  • @BrianGoetz I'm going to have to precede my reply with the fact that [the javadocs of `Stream.forEach` says "Performs an action for each element of this stream. \[...\] The behavior of this operation is explicitly nondeterministic."](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#forEach-java.util.function.Consumer-). A lack of guarantee is not a lack of assurance in using `forEach` in non-parallel streams. zeroflag linked a relevant example on how this may make a difference, but it explicitly uses parallel streams, where Streams as representations of Iterables... – Unihedron Mar 17 '15 at 15:36
  • ... are implemented as Spliterators within StreamSupport that invokes the underlying forEach operation of the Iterable. While the current implementation of `Stream.forEach` can change over time, so does `Iterable.forEach`. That doesn't hit the problem asked at all. – Unihedron Mar 17 '15 at 15:39
  • 2
    `Stream.forEach` *does not* call `Iterable.forEach` with the current implementation. This can be proved easily: `new ArrayList(){ @Override public void forEach(Consumer super String> c) { System.out.println("Iterable.forEach"); } }.stream().forEach(x->{});` will *not* print the message… – Holger Mar 17 '15 at 16:42