Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Java, what are the advantages of streams over loops? [closed]

People also ask

What advantages we gain from stream API?

The stream API allows you to perform operations on collections without external iteration. In this case, we're performing a filter operation which will filter the input collection based on the condition specified.

Are Java streams better than loops?

Conclusion: If you have a small list; for loops perform better, if you have a huge list; a parallel stream will perform better. And since parallel streams have quite a bit of overhead, it is not advised to use these unless you are sure it is worth the overhead.

Why are streams better than for loops?

Advantages of the streams:Your stream-handling code doesn't need to know the source of the stream or its eventual terminating method. Streams can succinctly express quite sophisticated behavior. Streams can be a replacement for looping because they allow for the processing of a sequence of data (similarly to a loop).

What are the advantages of streams in Java?

There are a lot of benefits to using streams in Java, such as the ability to write functions at a more abstract level which can reduce code bugs, compact functions into fewer and more readable lines of code, and the ease they offer for parallelization.


Interesting that the interview question asks about the advantages, without asking about disadvantages, for there are are both.

Streams are a more declarative style. Or a more expressive style. It may be considered better to declare your intent in code, than to describe how it's done:

 return people
     .filter( p -> p.age() < 19)
     .collect(toList());

... says quite clearly that you're filtering matching elements from a list, whereas:

 List<Person> filtered = new ArrayList<>();
 for(Person p : people) {
     if(p.age() < 19) {
         filtered.add(p);
     }
 }
 return filtered;

Says "I'm doing a loop". The purpose of the loop is buried deeper in the logic.

Streams are often terser. The same example shows this. Terser isn't always better, but if you can be terse and expressive at the same time, so much the better.

Streams have a strong affinity with functions. Java 8 introduces lambdas and functional interfaces, which opens a whole toybox of powerful techniques. Streams provide the most convenient and natural way to apply functions to sequences of objects.

Streams encourage less mutability. This is sort of related to the functional programming aspect -- the kind of programs you write using streams tend to be the kind of programs where you don't modify objects.

Streams encourage looser coupling. Your stream-handling code doesn't need to know the source of the stream, or its eventual terminating method.

Streams can succinctly express quite sophisticated behaviour. For example:

 stream.filter(myfilter).findFirst();

Might look at first glance as if it filters the whole stream, then returns the first element. But in fact findFirst() drives the whole operation, so it efficiently stops after finding one item.

Streams provide scope for future efficiency gains. Some people have benchmarked and found that single-threaded streams from in-memory Lists or arrays can be slower than the equivalent loop. This is plausible because there are more objects and overheads in play.

But streams scale. As well as Java's built-in support for parallel stream operations, there are a few libraries for distributed map-reduce using Streams as the API, because the model fits.

Disadvantages?

Performance: A for loop through an array is extremely lightweight both in terms of heap and CPU usage. If raw speed and memory thriftiness is a priority, using a stream is worse.

Familiarity.The world is full of experienced procedural programmers, from many language backgrounds, for whom loops are familiar and streams are novel. In some environments, you want to write code that's familiar to that kind of person.

Cognitive overhead. Because of its declarative nature, and increased abstraction from what's happening underneath, you may need to build a new mental model of how code relates to execution. Actually you only need to do this when things go wrong, or if you need to deeply analyse performance or subtle bugs. When it "just works", it just works.

Debuggers are improving, but even now, when you're stepping through stream code in a debugger, it can be harder work than the equivalent loop, because a simple loop is very close to the variables and code locations that a traditional debugger works with.


Syntactic fun aside, Streams are designed to work with potentially infinitely large data sets, whereas arrays, Collections, and nearly every Java SE class which implements Iterable are entirely in memory.

A disadvantage of a Stream is that filters, mappings, etc., cannot throw checked exceptions. This makes a Stream a poor choice for, say, intermediate I/O operations.


  1. You realized incorrectly: parallel operations use Streams, not Optionals.

  2. You can define methods working with streams: taking them as parameters, returning them, etc. You can't define a method which takes a loop as a parameter. This allows a complicated stream operation once and using it many times. Note that Java has a drawback here: your methods have to be called as someMethod(stream) as opposed to stream's own stream.someMethod(), so mixing them complicates reading: try seeing the order of operations in

    myMethod2(myMethod(stream.transform(...)).filter(...))
    

    Many other languages (C#, Kotlin, Scala, etc) allow some form of "extension methods".

  3. Even when you only need sequential operations, and don't want to reuse them, so that you could use either streams or loops, simple operations on streams may correspond to quite complex changes in the loops.


You loop over a sequence (array, collection, input, ...) because you want to apply some function to the elements of the sequence.

Streams give you the ability to compose functions on sequence elements and allow to implement most common functions (e.g. mapping, filtering, finding, sorting, collecting, ...) independent of a concrete case.

Therefore given some looping task in most cases you can express it with less code using Streams, i.e. you gain readability.


I'd say its parallelization that is so easy to use. Try iterating over millions of entries in parallel with a for loop. We go to many cpus, not faster; so the easier it is to run in parallel the better, and with Streams this is a breeze.

What I like a lot is the verbosity they offer. It takes little time to understand what they actually do and produce as opposed of how they do it.