• Post Reply Bookmark Topic Watch Topic
  • New Topic

streams boilerplate code

 
Henno Vermeulen
Ranch Hand
Posts: 40
2
Eclipse IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
AFAIK lambda's and streams were designed to help reduce boilerplate code and provide new possibilities such as parallel streams.
Still I feel it also adds boilerplate code in the common case when you are working with Lists/Collections:

  • At the beginning you have to explicitly call .stream() on a collection before using stream methods such as filter
  • You have to add code to explicitly convert a stream back to a List


  • E.g. to get a list of all even numbers in a list you would write something like:

    Why isn't it possible to do something like

    to do the same? Is there a shorter alternative to the first line of code?

    My guess is that this would require either a larger language change, or this would lead to combinatorial explosion of default methods in the collection interfaces. Am I right?
     
    Jason Bullers
    Ranch Hand
    Posts: 111
    8
    Clojure IntelliJ IDE Java
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Depends on what you mean when you say "adds boilerplate." Compared to the imperative approach, there's an awful lot less of that in this approach. I do agree that I wish we could do away with the call to stream() all the time, but I'm sure there was no easy way to completely reimagine the Collections API in a backward compatible way without creating a new abstraction.

    Let's pretend that we didn't have to call stream() to get something nice to work with, and instead we could just say:

    We can clean that up a little with static imports (to my eyes, a lot of those methods read better if you do that):

    Now the next step we'd want to get to your clean looking solution is to be able to drop that call to collect(). What does that mean about the filter() implementation? It would have to return a list of the same type as our original list (List<Integer> in this case). That's perfect for this case, but it's hardly the general case, and that seems to be what the Streams API is designed to handle. The filter() method also returns a Stream and you need to call some terminal operation on that Stream in order to finish up and realize the new collection.

    One big advantage to this is that everything is lazy; you can build up a bunch of operations and you won't get any unnecessary intermediate collections:

    You can think of it as if you got back a filtered list that only has even numbers, then squared all those numbers to generate a new list, then filtered that list further to keep only the squares greater than ten, but in reality, nothing has happened yet. When you finally ask for that to be collected as a list, the Stream can very efficiently do that for you, without all those intermediate steps.

    Now, all of that being said, why couldn't we just have a filter method in the List interface that just did that whole stream() and collect() business internally so we wouldn't have to? Your guess is as good as mine. I'd go with your second thought and say if you did that, where do you stop? How many cases like this do you capture with special "convenience" wrapper methods? I guess the designers decided it was better to avoid wrapping certain use cases of Streams and just give all that power straight to the client programmers, even if it does cost a few extra calls.
     
    Maurice Naftalin
    Author
    Greenhorn
    Posts: 20
    6
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Hi Henno

    You have two questions here: one about why filter (and other intermediate stream operations) weren't added directly to collections, and one about the collect method. You and Jason have the answer to the first one about right; although it wouldn't actually be a combinatorial explosion of methods on Collection or Iterable, it certainly would be a lot of methods! The Java 8 designers were very keen to create an API that would be easy to use, and cluttering a collections interface with stream operations would have been very confusing. An you need to consider that not only collections are involved; streams can be generated by a variety of means, so the same methods would have had to appear in a variety of places. It really wouldn't have made sense!

    As for collect, I think it's helpful because it emphasises that you need a Collector to dump the contents of a stream into a collection. The Collector API is one of the best things about streams, and IMO the confusion that would be created by hiding it with specialised collector methods added to Stream would be a high price to pay in order to save a single method call.

    Regards
    Maurice
     
    Henno Vermeulen
    Ranch Hand
    Posts: 40
    2
    Eclipse IDE Java
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Thank you both for your answers!

    Indeed I don't think the advantage of not typing "stream" outweighs the disadvantage of having to give each collection interface filter/map/etc... methods.
    And indeed it's good to be explicit about exactly what type you are collecting from a stream. And yes, I will be using the static import to shorten that a bit.
     
    • Post Reply Bookmark Topic Watch Topic
    • New Topic
    Boost this thread!