Java 8 Stream API
Introduced in Java 8 along with other programming features, Stream API provides various functionalities to process a collection of objects.
Before we dive into Streams API, let’s see for an example, how we used to iterate a List and calculate the sum before the introduction of Streams in Java 8.
List<Integer> numList = Arrays.asList(2,5,7,9,10,20,30,40);
int sum = 0;
Iterator<Integer> iterator = numList.iterator();
while (iterator.hasNext()) {
int number = iterator.next();
if (number< 10) {
sum += number;
}
}
The above codes works perfectly fine and the variable sum will have the value 23 as the sum of numbers less than 10, but there are few issues with this code.
- The code has to be written on how the list should be iterated although the requirement is just to obtain the sum of the numbers which are less than 10
- The program is sequential
- A lot of code must be written to perform this simple calculation.
To avoid the above issues, Stream API was introduced in Java 8 which takes control of the iteration internally, meaning it can iterate the elements itself.
The above lines of code can be boiled down to just one line as shown below by using lambda expressions in conjunction with the Stream API as most of the method arguments are functional interfaces.
int sum = numList.stream().filter(num -> num < 10).mapToInt(num -> num).sum();
As you must have a slight understanding at least on what the streams API can do, let’s look at the features of Java Stream and some commonly used methods.
Streams
A Stream is a sequence of objects that operates on a source data structure and produce pipelined data that can be used to perform various operations.
- A Stream is not a data structure instead it uses a source such as a Collection or an array to produce a pipeline of data using various computational operations.
- A Stream is functional in nature, which means that it does not modify its source when producing the result.
- Many operations such as filter(), map() and sorted() can be implemented lazily allowing high performance.
- Streams support sequential as well as parallel processing.
- Since data is on-demand when processing streams, it is not possible to create a reference for future use as Java Streams are consumable (cannot reuse the same stream multiple times).
A Stream can be obtained in a various ways such as
- By calling
stream()
andparallelStream()
methods on a Collection. - By invoking
Arrays.stream(Object[])
on an Array. - Using the factory method on Stream classes such as
Stream.of(Object[])
. Random.ints()
to generate a Stream of random numbers.
Stream Operation
Stream operations are classified into 2 types, Intermediate and Terminal Operations which are then combined to create a Stream pipeline.
Intermediate Operation
Intermediate Operation return a new Stream, for example when the filter()
method is called, it does not do an actual filtering, whereas it creates a new Stream which will contain the elements of the initial stream that matches the given predicate when traversed.
Let’s go through some of the intermediate operation in Stream API.
map()
operation can be used to map a source such as a collection or an array to other objects depending on the function passed as an argument.
List<Integer> numList = Arrays.asList(2,5,7,9,10,20,30,40);List<Integer> multipliedList = numList
.stream()
.map(num -> num*2)
.collect(Collectors.toList());
The above code will create a List of Integers which will contain the value of each element of numList multiplied by 2.
[4, 10, 14, 18, 20, 40, 60, 80]
2. filter()
operation is used to select the elements that matches the given predicate.
List <String> nameList = Arrays.asList("Bruce", "Wayne", "Ben", "Affleck");
List<String> filteredList = nameList
.stream()
.filter(name -> name.startsWith("B"))
.collect(Collectors.toList());
The above block of code will create a new List that contains the names that start with ‘B’
[Bruce, Ben]
3. sorted()
operation as the name suggests, can be used to sort a stream.
List <String> nameList = Arrays.asList("Bruce", "Wayne", "Ben", "Affleck");List<String> sortedList = nameList
.stream()
.sorted()
.collect(Collectors.toList());
Calling the sorted operation on the nameList after converting into a stream will sort the names in their natural order.
[Affleck, Ben, Bruce, Wayne]
Terminal Operations
Terminal operations in Streams Interface typically return a single value unlike Intermediate operations return another stream. When a terminal operation is invoked, the iteration of the stream will start and then a single result of the terminal operation will be returned.
Some of the commonly used terminal operations are,
collect()
operation is simply used to collect the elements in a stream. You might have noticed that in the above intermediate operations, this method is called so that it will be collected to a List rather than a stream.
2. anyMatch()
operation takes a single Predicate as an argument. If the predicate returns true for at least one element, then the anyMatch() operation will return true.
List <String> nameList = Arrays.asList("Bruce", "Wayne", "Ben", "Affleck");Boolean isBenExist = nameList
.stream()
.anyMatch(name -> name.equals("Ben"));
The above operation will return True since ‘Ben’ is available in the nameList.
3. findFirst()
operation ideally returns the first element in a stream as an Optional type.
List <String> nameList = Arrays.asList("Bruce", "Wayne", "Ben", "Affleck");Optional<String> firstElement = nameList
.stream()
.filter(name -> name.startsWith("B"))
.findFirst();
System.out.println(firstElement.isPresent() ? firstElement.get() : "Not Found");
The above operations will filter the stream and find the elements that starts with ‘B’ and then the findFirst() will return the first element of the filtered stream.
Bruce
Comments
Post a Comment