Java 8 has introduced a new feature called Stream API. The Stream API along with lambda expressions can be used to perform bulk operations on a sequence of elements.
What is a Stream?
A Stream is nothing but a sequence of values. It can consist of primitive or object types. It can use an array, collection or simply a list of values as the underlying data source. So streams sit on top of the underlying data source and help to apply operations on the elements on the underlying data source. However, it is important to note that they do not modify the underlying data source. Java 8 added the java.util.Stream package to support streams.
Why were Streams added
The Java Collection framework was added many years back and does not take advantage of recent hardware enhancements like multiple cores. Not only that, most of the times, collections are used to apply some function to a set of values or to summarise them. So if a number of operations are performed sequentially, it would result in a lot of intermediate variables, to store the result of processing. The “Pipes and Filters” pattern, is useful in such scenarios as it eliminates the need for intermediate variables. The Stream API applies pipeline processing and allows parallel execution which takes advantage of recent hardware enhancements.
Types of Stream Operations
There are 2 types of Streams operations:
Intermediate
Intermediate operations operate on a Stream and return a Stream instance. Intermediate operations can be chained to perform a series of operations. An example of an intermediate operation is filter; it filters the input stream based on the specified condition.
Terminal
Terminal operations operate on streams but return a non-stream result. They terminate a method chain. An example of a terminal operation is anyMatch which checks if any element in the input stream matches the condition specified and if so returns a boolean value.
Chaining Operations
Streams allow you to chain a number of operations. So each operation is performed on the output of the previous operation. This makes the Stream API very powerful. The following code demonstrates this:
List<String> names = Arrays.asList(“Sunday”,“Monday”,“January”,“February”,“Friday”);
names.stream().filter(name -> name.endsWith(“day”)).sorted().map(name -> name.toUpperCase()).forEach(name -> System.out.println(name));
In this code, the filter, sort and map operations are chained. So the input list is first filtered to find the strings that are days of the week, this list is then sorted and then converted to uppercase.
When this code is executed, it will print the following output:
FRIDAY
MONDAY
MONDAY
SATURDAY
SUNDAY
WEDNESDAY
Examples of Stream Operations
The following table lists some examples of Stream operations
Stream Operation Name |
Type |
What it does |
Example |
filter |
Intermediate |
Filters the input stream based on the specified condition |
numbers.stream().filter(num -> num %2 == 0) (Creates a stream that has only even numbers) |
map |
Intermediate |
Applies some function to each element of a stream |
input.stream().map(str -> “Hello “+str) (Appends the text “Hello” to each element) |
sorted |
Intermediate |
Sorts the elements in the input stream as per their natural order |
input.stream().sorted() (Sorts the input stream) |
anyMatch |
Terminal |
Checks if there is any element in the input stream that matches the specified condition |
boolean flag = input.stream().anyMatch(str -> str.endsWith(“day”)) (Checks if there is any String in the input stream that ends with “day”) |
count |
Terminal |
Returns the number of elements in the stream |
Long l = input.stream().count() (Returns the number of elements in the input stream) |
Parallel stream operations
The Stream API supports parallel streams. Parallel streams allow executing stream operations concurrently on the elements in a stream. When parallel streams are used on streams with a large number of elements, there is a huge performance improvement provided the underlying platform supports parallel programming.
Stream Ordering
A stream may be ordered or unordered, depending on the underlying data source used. So if the underlying data source is a List or an array, the corresponding stream is ordered, however if the underlying data source is a Set, the corresponding Stream is unordered. Most intermediate and terminal operations preserve the ordering in the stream.
Converting Stream to a Collection
There is a method available on the Stream interface called collect. It can be used to convert a Stream to a List,Set, Map or Collection. This method accepts an argument of type Collector. The collect method reduces the input stream to an object of any data type based on the Collector specified.
Conclusion
The Stream API is a very powerful API that can be used to process a sequence of elements. They help to make the code concise and increase the readability of the code.
Leave a Reply