Pages

Finally Java 8 is here, after more than 2 years of JDK 7, we have a much expected Java 8 with lots of interesting feature. Though Lambda expression is the most talked item of coming Java 8 release, it wouldn't have been this much popular, if Collections were not improved and Stream API were not introduced. Java 8 is bringing on new Streams API java.util.stream package, which allow you to process elements of Java Collections in parallel. Java is inheritably sequential and there is no direct mean to introduce parallel processing at library level, stream API is going to fill that gap. By using this, you can filter elements of collection on given criterion e.g. if you have a List of orders, you can filter buy orders with sell orders, filter orders based upon there quantity and price and so on.
You can also perform two of most popular functional programming functions e.g. mapand reduce.java.util.stream class provides function such mapToInt(), mapToLong(), and map function to apply an operation on all elements of Collections.

By the way, these are just few of gems of Stream API, I am sure you will find several more, when you start exploring lambda expression and java.util.stream package. In this tutorial, we will see 2 examples of using Java 8 Stream with Collections classes.

I have chosen List for these example, but you can use any Collection e.g. Set, LinkedList etc.

By the way, use of Stream is not limited to Collections only, you can even use an array, a generator function, or an I/O channel as source. In most cases, a Stream pipeline in Java 8 consists a source, followed by zero or more intermediate stream operations e.g. filter() or map(); and a terminal operation such as forEach() or reduce().

How to use Streams with Collections in Java 8

As I said, we can use Stream with Collection as source in previous paragraph, now is time to see some code in action. For this example, I have a list of Orders, where each order contains bare minimum details e.g. Side (buy or sell), price, quantity and security. Once we initialized our list with some orders, we can perform interesting operations exposed by stream API. In first example, we are using filter() method to filter all sell orders. In second example, we are using Stream APIs mapToDouble() method to calculate total for both price and quantity, which would have mean iterating over Collection and adding each elements price in total. Because of Java 8 lambda expression and stream API our code is reduced into pretty much one liner.

Collection interface is enhanced to provide stream support. It now has a stream() method which returns a sequential Stream with this collection as its source. Once you get the reference of stream, you can perform bulk data operations with this collection.

2) One of the important think to note is that Stream do not modify the original source. For every operation, a new Stream is created and original collection remains unmodified. Similarly, you cannot reuse Stream either. Reusing a closed stream will throw IllegalStateException as shown below :

Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.(AbstractPipeline.java:203)
at java.util.stream.ReferencePipeline.(ReferencePipeline.java:94)
at java.util.stream.ReferencePipeline$StatelessOp.(ReferencePipeline.java:618)
at java.util.stream.ReferencePipeline$2.(ReferencePipeline.java:163)
at java.util.stream.ReferencePipeline.filter(ReferencePipeline.java:162)
at Test.main(Test.java:33)

3) Stream operations are mainly divided into two categories : intermediate and terminal operations. Intermediate operations such as filter() or map() returns a new Stream, while terminal operations such as Stream.forEach() produce a result or side effect. After the terminal operation, the stream pipeline is considered consumed, and can no longer be used.

4) Intermediate operations are also of two types stateless and stateful. As name suggests, stateless operations doesn't retain any state from previously processed element, filter() and map() are two examples of stateless intermediate operation. On the other hand distinct() and sorted() are example of stateful operations, which can have state from previously processed elements, while processing new elements.

That's all about how to use Streams with Collections in Java 8. In my opinion, Stream will going to be hugely popular among Java developers, given it's supports of functional programming idioms such as map and reduce along with parallel processing capability, support for filtering elements from Collection. Lambda expression and Stream API will also help in removing lots of boiler plate code, which was generated due to usage of anonymous inner class. If you still not downloaded Java 8 release, you can download from here.

Thanks for reading this article so far. If you like this article then please share with your friends and colleagues. If you have any question, doubt, or feedback then please drop a comment and I'll try to answer your question.

P.S. : If you want to learn more about new features in Java 8 then please see the tutorial What's New in Java 8. It explains about all important features of Java 8 e.g. lambda expressions, streams, functional inteface, Optionals, new date and time API and other miscelleneous changes.