50
for (Canvas canvas : list) {
}

NetBeans suggests me to use "functional operations":

list.stream().forEach((canvas) -> {
});

But why is this preferred? If anything, it is harder to read and understand. You are calling stream(), then forEach() using a lambda expression with parameter canvas. I don't see how is that any nicer than the for loop in the first snippet.

Obviously I am speaking out of aesthetics only. Perhaps there is a technical advantage here that I am missing. What is it? Why should I use the second method instead?

Saturn
  • 3,887
  • 9
  • 30
  • 40
  • 3
    https://netbeans.org/bugzilla/show_bug.cgi?id=241035 – gnat Sep 13 '15 at 19:18
  • 18
    In your particular example, it would not be preferred. – Robert Harvey Sep 13 '15 at 20:42
  • 1
    As long as the only operation is a single forEach, I tend to agree with you. As soon as you add other operations to the pipeline, or you produce an output sequence, then the stream-approach becomes preferable. – JacquesB Dec 01 '15 at 21:56
  • @RobertHarvey wouldn't it? why not? – sara Jun 22 '16 at 18:07
  • @RobertHarvey well I agree the accepted answer really shows how the for-version gets blown out of the water for more complicated cases, but I don't see why for "wins" in the trivial case. you state it like it's self-evident but I don't see it, so I asked. – sara Jun 22 '16 at 18:13
  • @kai: Netbeans is a good tool, but like any tool, sometimes it gets its suggestions wrong. I routinely ignore refactoring suggestions from Resharper when there is a good reason for why I've chosen to write the code the way I have. – Robert Harvey Jun 22 '16 at 18:16
  • @RobertHarvey yes I know, what I didn't see was why you thought this was such a case. – sara Jun 22 '16 at 18:17
  • 1
    @kai: Counter-question: would you use the Netbeans version exclusively, just because Netbeans says so? Why or why not? In the OP's example, he doesn't use Stream's features, so there's no compelling reason to use Netbeans' version. If you don't need the advanced features, simpler and clearer *always* wins. – Robert Harvey Jun 22 '16 at 18:19
  • @RobertHarvey I personally tend to prefer functional style code (although I rarely work in Java nowadays), so function calls are more appealing to me than special language constructs like `for`-loops, but I know that's highly subjective. It sounded to me like you thought there was some obvious thing that made the `for`-version objectively better, so I wondered why you think that. Maybe this isn't the place for this discussion though ... – sara Jun 22 '16 at 18:23
  • @kai: Let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/41520/discussion-between-robert-harvey-and-kai). – Robert Harvey Jun 22 '16 at 18:23
  • https://jaxenter.com/java-performance-tutorial-how-fast-are-the-java-8-streams-118830.html – SedJ601 Sep 08 '17 at 14:37

4 Answers4

50

Streams provide much better abstraction for composition of different operations you want to do on top of collections or streams of data coming in. Especially when you need to map elements, filter and convert them.

Your example is not very practical. Consider the following code from Oracle's site.

List<Transaction> groceryTransactions = new Arraylist<>();
for(Transaction t: transactions){
    if(t.getType() == Transaction.GROCERY){
        groceryTransactions.add(t);
    }
}
Collections.sort(groceryTransactions, new Comparator(){
    public int compare(Transaction t1, Transaction t2){
        return t2.getValue().compareTo(t1.getValue());
    }
});
List<Integer> transactionIds = new ArrayList<>();
for(Transaction t: groceryTransactions){
    transactionsIds.add(t.getId());
}

can be written using streams:

List<Integer> transactionsIds = 
    transactions.stream()
                .filter(t -> t.getType() == Transaction.GROCERY)
                .sorted(comparing(Transaction::getValue).reversed())
                .map(Transaction::getId)
                .collect(toList());

The second option is much more readable. So when you have nested loops or various loops doing partial processing, it's very good candidate for Streams/Lambda API usage.

luboskrnac
  • 896
  • 8
  • 10
  • 8
    There are optimization techniques like *map fusion* (`stream.map(f).map(g)` ≡ `stream.map(f.andThen(g))`) *build/reduce fusion* (when building a stream in one method and then passing it to another method which consumes it, the compiler can eliminate the stream) and *stream fusion* (which can fuse many stream ops together into a single imperative loop), which can make stream operations much more efficient. They are implemented in the GHC Haskell compiler, and also in some other Haskell and other functional language compilers, and there are experimental research implementations for Scala. – Jörg W Mittag Sep 14 '15 at 03:05
  • Performance probably isn't a factor when considering functional vs loop as surely the compiler could/should do the conversion from a for loop to a functional operation if Netbeans can and it is determined to be the optimal path. – Ryan Apr 11 '17 at 18:12
  • 1
    I would disagree that the second is more readable. It takes a while to figure out what's going on. Is there a performance advantage to doing it the second method because otherwise I don't see it? – Bok McDonagh Jan 18 '19 at 03:07
  • 3
    @BokMcDonagh, Me experience is that it is less readable for developers that didn't bother to get familiar with new abstractions. I would suggest to more use such APIs, to get more familiar, because it is the future. Not only in Java world. – luboskrnac Jan 18 '19 at 08:13
18

Another advantage of using the functional streaming API is, that it hides implementation details. It only describes what should be done, not how. This advantage becomes obvious when looking at the change that needs to be done, to change from single threaded to parallel code execution. Just change the .stream() to .parallelStream().

Stefan Dollase
  • 281
  • 1
  • 7
15

If anything, it is harder to read and understand.

That is highly subjective. I find the second version much easier to read and understand. It matches how other languages (e.g. Ruby, Smalltalk, Clojure, Io, Ioke, Seph) do it, it requires fewer concepts to understand (it's just a normal method call like any other, whereas the first example is specialized syntax).

If anything, it's a matter of familiarity.

Jörg W Mittag
  • 101,921
  • 24
  • 218
  • 318
-2

Personally, More abstration = harder to program. So i will go with the first and truly lamda is the worst that programmers created. it just complicates things. Clearly it is usefull in some cases but, it is far less readable and i take about 3 times more to be able to understand it. I think they make it to "look cool" May be mistaken but i truly doubt it has any real application. (excpet to pretend you are better)

Fox
  • 19
  • Do you do all your programming by writing the bytes for the executable directly, or do you in fact use a higher level **more abstract** programming language and tools? – Philip Kendall Sep 30 '21 at 14:01
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Sep 30 '21 at 14:50