Stream API
An API(Application Programming Interface) represents a group of classes and interfaces using which a programmer can develop applications or software, In Java 8, a new package java.util.stream has been provided for creating and working with streams,
We know that a stream represents flow of data from one place to another place, this definition is slightly changed in Java 8, Now a stream represents flow of elements(objects) on which manipulation can be done, The primary aim of a stream concept is to make the operations easy on collections, A collection represents an object that contains a group of other objects, In case of collection, first the objects are planned properly and then stored in the collection, It means a collection contains objects, when we need to make some manipulation on the objects of a collection, we can use methods that are already available in the collection framework but we cannot use lambda expressions in this case,
Lambda expressions are developed to make manipulation on the objects easy, To utilize this advantage we should collect all the objects of a collection into a stream and then apply manipulations through lambda expressions, it means that a stream contains objects coming from a collection that can be easily manipulated with the help of lambda expressions, this is the reason streams are generally used with collections like lists or sets,
So what is difference between a collection and a stream?
So a collection refers to a group of objects in memory and a stream contains objects that are taken from a collection for the purpose of doing some manipulation,
Creating streams:
To create a stream from a list, we can use a stream() method of Stream class of java.util.stream package, for example, Stream<Integer> sm=lst.stream();
here lst refers to an object of ArrayList that contains several integer objects, to convert this ArrayList into a stream we are using the lst.stream() method that returns a Stream class object sm, once the stream is created like this, we can use manipulation functions on the stream to obtain the desired result, please remember that the stream() method is not actually converting the ArrayList into stream but it is only retrieving the elements from the ArrayList into the stream object sm, we can use a manipulation function filter() like this sm.filter(i -> i>5)
Observe the lambda expression that is passed to the filter() function which returns a stream that contains numbers greater than 5, we can collect all these elements into a list object using collect() function like this:
List<Integer> lst1=sm.filter(i -> i>5).collect(Collectors.toList());
The collect() function takes an argment Collectors.toList() this is responsible for converting the stream into list, let’s take an example:
List<Integer> lst = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
lst.add(i);
}
System.out.println(lst);
// convert this list into stream using stream()
Stream<Integer> sm = lst.stream();
// filter the elements which are greater than 5 and collect them using
// Collectors.toList()
List<Integer> lst1 = sm.filter(i -> i > 5).collect(Collectors.toList());
// Display the new list
System.out.println(lst1);
Ways of creating Streams:
Stream class’s stream() method creates streams from lists or sets, there are other methods also which creates streams from other sources, for example the Stream.of() method takes some values or an array and constructs a Stream object
Stream<Integer> sm1=Stream.of(10,11,12,13,14,15);
Stream<Integer> sm2=Stream.of(arr); // arr is Integer type array
In the first statement the numbers from 10 to 15 actually represent Integer objects containing these numbers, these Integer objects are stored as elements in the Stream object sm1, now to display the elements we can use forEach()method as:
sm1.forEach(System.out::println);
here double colon(::) operator is used to connect the println() method with System.out, the forEach() method is useful to perform a specified action on each element of the stream, here the action that is used to perform is to display(i.e println)
Lets understand how to create a streams of objects using the Stream.of() method
// Create a stream of Integer objects using Stream.of() method
Stream<Integer> sm1 = Stream.of(10, 11, 12, 13, 14, 15);
// Display the elements of stream
sm1.forEach(System.out::println);
// Create another stream of array of Float objects
Float arr[] = { 1.1f, 2.2f, 3.3f, 4.4f, 5.5f };
Stream<Float> sm2 = Stream.of(arr);
// Display the elements of stream
sm2.forEach(System.out::println);
Operations on a stream:
Stream class offers several methods to manipulate objects, there are two types of operations which can be done on stream,
Intermediate operations: These operations returns a stream hence we can use other methods on stream to get another stream as result, in this way it is possible to use a chain of methods Example: filter(), map(), sorted()
Terminal operations: These operations return a value of a certain type Example: forEach(), count(), collect()
So what is the difference between Intermediate Operations and Terminal Operations in stream?
Intermediate operations return a stream as a result of the operation but the Terminal operations return only a value as the result.
filter() method : This method accepts a predicate and filter the stream based on the condition given in the predicate, we can also use Lambda Expressions in place of Predicate then it returns the resultant stream, since the result is a stream, we can use another function to process it,
For example:
long n=lst.stream().filter(x -> x.length()>4).count();
Here lst represents the list of object which is converted into a stream by stream() method, This method returns a stream on which filter() method is acting to find all the strings which are having more than 4 characters length, this result returned by filter() method is again a stream on which count() method is acting to count these strings and return a long integer.
map() method : This method convert each element into another object according to the given function, for example the following map() method converts each strings into uppercase using String class toUpperCase() method
names.stream().filter((s) -> s.startWith(“A”)) .map(x -> x.toUpperCase()) .forEach(System.out::println);
Here names represents an ArrayList that contains a group of a strings, this is converted into a stream with the help of a stream() method, on this stream filter() method acts and filters the strings that start with ‘A’, then map() method acts on the stream returned by filter() method and converts all those strings into uppercase, finally forEach() method is displaying those strings,
sorted() method : This method sorts the elements in ascending order and returns the sorted stream, if we want to sort the elements in descending order then we should use comparator, observe the following example: lst.stream().sorted().map(x -> x.toUpperCase()).collect(Collectors.toList());
Here lst is converted into a stream by stream() method and then sorted by sorted() method, then map() method converts the strings into uppercase, finally collect() method collects these strings and returns them as a list object,
forEach() method : This method takes lambda expression and apply that expression to each element of the stream, it does not return any result, for example the following forEach() method displays all the names of the stream ‘names’
names.stream().forEach(System.out::println);
count() method : This method returns a long integer value, it counts the number of elements in the stream, for example:
long n=lst.stream().filter(x -> x.startWith(“U”)).count();
here lst represents a list of strings that is being converted into a stream by stream() method, the filter() method acts on the stream and filters the strings starting with ‘U’ then count() method counts such strings and returns the result into long type n
collect() method : This method collects the elements from a stream and stores them in a collection as indicated by its arguments, for example:
lst.stream().sorted().map(x -> x.toUpperCase()).collect(Collectors.toList());
In this statement, after sorting the element, map() method converts each element into uppercase and then the collect() method collects those elements into a list as indicated by Collectors.toList()
Let’s see an example:
package com.java8features.streams;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamDemo {
public static void main(String[] args) {
List<String> lst = Arrays.asList(“USA”, “Japan”, “India”, “China”, “”, “Russia”, “UK”);
long n = lst.stream().filter(x -> x.length() > 4).count();
System.out.println(“Number of countries with length more than 4: “ + n);
List<String> lst1 = lst.stream().filter(x -> !x.isEmpty()).collect(Collectors.toList());
System.out.println(“List after removing the empty string: “ + lst1);
List<String> lst2 = lst1.stream().sorted().map(x -> x.toUpperCase()).collect(Collectors.toList());
System.out.println(“The list after sorting in upper case: “ + lst2);
String[] arr = lst2.stream().map(x -> x.toUpperCase()).toArray(String[]::new);
System.out.println(“Array of sorted string in uppeercase:”);
for (String country : arr) {
System.out.println(country);
}
}
}
OUTPUT:
Number of countries with length more than 4: 4
List after removing the empty string: [USA, Japan, India, China, Russia, UK]
The list after sorting in upper case: [CHINA, INDIA, JAPAN, RUSSIA, UK, USA]
Array of sorted string in uppeercase:
CHINA
INDIA
JAPAN
RUSSIA
UK
USA
Here comes the end...See You…Keep learning…Keep Sharing