Java 8 - Stream API

Introduction

 
With reference to the article, Introduction to Java 8, Java 8 consists of various interesting features and one of them is Stream API.
 
A stream is an interface and can be defined as a sequence of elements from a source (Collections) that support aggregate operations. Stream operations are divided into intermediate and terminate operations.
 
Intermediate operations return a new stream. Executing an intermediate operation will result in creating a new Stream().
 
Terminal operations return the final output, such as Stream.forEach() will traverse to a stream to produce a result.
 
These operations are combined to form a stream pipeline, it consists of a source (Collections), followed by zero or more intermediate operations and terminal operations.
 
Example
  1. Source - Filter - Collect
  2. Source - Filter - Map - Collect
Here, a source can be collections, sets, lists, or arrays.
 
Intermediate operations include methods like map, filter, sorted, and terminal operations include methods like forEach, collect, count, reduce, etc
 

Obtaining a stream

 
There are various methods to obtain a stream, some of them are listed below.
  • Collection via stream() and parallelStream() methods.
Example
  1. public class StreamorParallelStream {  
  2.     public static void main(String[] args) {  
  3.         List < String > list = Arrays.asList("Sunday""Monday""Tuesday""Wednesday""Thursday""Friday""Saturday");  
  4.         //Ordered Result  
  5.         list.stream().forEach(System.out::println);  
  6.         //Unordered Result  
  7.         list.parallelStream().forEach(System.out::println);  
  8.     }  
  9. }  
array via Arrays.stream(Object[]) and,
 
Example
  1. public class ArraysStream {  
  2.     public static void main(String[] args) {  
  3.         List < Integer > list = Arrays.asList(0123456789);  
  4.         Integer[] num = (Integer[]) list.toArray();  
  5.         Arrays.stream(num).forEach(System.out::println);  
  6.     }  
  7. }  
Static factory methods like Stream.of(Object[]), Stream.iterate(Object , Unary Operator)
  1. public class StreamOfFunction {  
  2.     public static void main(String[] args) {  
  3.         Stream < String > streamOf = Stream.of("Monday""Tuesday""Wednesday");  
  4.         List < String > list = streamOf.collect(Collectors.toList());  
  5.         System.out.println(list);  
  6.     }  
  7. }  

Methods in Stream Interface

 
Stream interface includes various methods where the implementation of various abstract methods is available in java.util.stream.ReferencePipeline class. It is an abstract class that implements a stream interface. Some of them are explained below:
 
filter()
 
Declaration : Stream<T> filter(Predicate<? super T> predicate)
 
The intermediate method filter() can be used to filter out elements from a stream and returns a new stream consisting of the elements that match the given condition of the given predicate(condition).
 
An element is entered into the stream only if the predicate returns true for that element.
 
Example
  1. public class FilterMethod {  
  2.     public static void main(String[] args) {  
  3.         List < Integer > list = Arrays.asList(0123456789);  
  4.         List < Integer > listnew = list.stream().filter(i - > (i % 2 == 0)) //Intermediate method  
  5.             .collect(Collectors.toList()); //Terminating method  
  6.         .forEach(System.out::println);  
  7.     }  
  8. }  
map()
 
Declaration : <R> Stream<R> map(Function<? super T,? extends R> mapper)
 
Here, R is a type of element for a new stream.
 
The intermediate method map() converts each element present in the stream into another object via the given function and returns a new stream consisting of the results of applying the given function to each element of the stream.
 
An element is entered into the stream only if the predicate returns true for that element.
 
Example
  1. public class MapMethod {  
  2.     public static void main(String[] args) {  
  3.         List < Integer > list = Arrays.asList(855126542758096105);  
  4.         System.out.println(list.stream() //convert list to stream  
  5.             .filter(i - > i % 5 == 0).map(i - > i * i) // Doubles each value  
  6.             .findFirst() //returns very first value  
  7.         }  
  8.     }  
flatmap()
 
Declaration :<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
 
Here, R is a type of element for new streams and mapper is a non-interfacing, stateless function.
 
The intermediate method flatmap() maps a single element into multiple elements, it has the effect of applying a one-to-many transformation to the elements of the stream and then flattening the result elements to the new stream.
 
Example
  1. public class FlatMapMethod {  
  2.     public static void main(String[] args) {  
  3.             List < String > list = Arrays.asList(“Today is the third weekend of July”);  
  4.             System.out.println(list.stream() //convert list to stream  
  5.                     .flatmap((value) - > {  
  6.                             String[] splitted = value.split(““);  
  7.                             return (Arrays.aslist(splitted.stream();  
  8.                             }).forEach(System.out::println);  
  9.                         }  
  10.                     }  
In the above example, the mapper function passed through flatmap, splits a line using a simple regular expression, into an array of words, and then creates a stream of words from an array.
 
distinct()
 
Declaration :Stream<T> distinct()
 
The intermediate method distinct() method returns a new stream consisting of the distinct elements. All duplicates values are eliminated.
 
Example
  1. public class DistinctMethod {  
  2.     public static void main(String[] args) {  
  3.         List < Integer > list = Arrays.asList(0124420134);  
  4.         List < Integer > listnew = list.stream().distinct() //Intermediate method  
  5.             .collect(Collectors.toList()); //Terminating method  
  6.         .forEach(System.out::println);  
  7.     }  
  8. }  
limit()
 
Declaration : Stream<T> limit(long maxSize)
 
Here, maxsize is the number of elements a stream is limited to.
 
The intermediate method limit() is a cheap operation especially for large values of maxsize, as limit(n) is fixed to return not only just n elements but first n elements. This function returns a stream consisting of elements limited to maxsize.
 
Example
  1. public class LimitMethod {  
  2.     public static void main(String[] args) {  
  3.         List < Integer > list = Arrays.asList(0124420134);  
  4.         List < Integer > listnew = list.stream().limit(3//Intermediate method  
  5.             .forEach(System.out::println); //Terminating method  
  6.     }  
  7. }  
sorted()
 
Declaration : Stream<T> sorted()
 
The intermediate method sorted() returns a new stream consisting of the elements of the original stream but in a sorted manner, sorting is performed in the natural order. Stability of sorting depends upon the nature of stream i.e ordered or unordered.
 
Example
  1. public class SortedMethod {  
  2.     public static void main(String[] args) {  
  3.         List < Integer > list = Arrays.asList(9153728406);  
  4.         List < Integer > listnew = list.stream().sorted() //Intermediate method  
  5.             .forEach(System.out::println); //Terminating method  
  6.     }  
  7. }  
peek()
 
Declaration : Stream<T> peek(Consumer<? super T> action)
 
Here, the action is a type of input argument to the function.
 
The intermediate method peek() takes input arguments of consumer type and returns a new stream consisting of the elements of the original stream performing the provided action on each element from the resulting stream.
 
Example
  1. public class PeekMethod {  
  2.     public static void main(String[] args) {  
  3.         List < Integer > list = Arrays.asList(0123456789);  
  4.         List < Integer > listnew = list.stream().peek((i) - > {  
  5.             System.out.println(“i”);  
  6.         });  
  7.     }  
  8. }  
collect()
 
Declaration : <R,A> R collect(Collector<? super T,A,R> collector)
 
Here, R is the type of result and A is the intermediate accumulation type of collector
 
The terminal method collect() takes the collector as an argument, and is used to perform operations like packing the elements into a data structure and returns the result of the reduction.
 
Example
  1. public class StreamOfFunction {  
  2.     public static void main(String[] args) {  
  3.         Stream < String > streamOf = Stream.of("Monday""Tuesday""Wednesday");  
  4.         List < String > list = streamOf.collect(Collectors.toList());  
  5.         System.out.println(list);  
  6.     }  
  7. }  
count()
 
Declaration : long count()
 
The terminal method count(), iterates internally through the stream and counts the elements. Is a special case of reduction and returns a long value which gives the count of the elements in the stream.
 
Example
  1. public class CountMethod {  
  2.     public static void main(String[] args) {  
  3.         List < Integer > list = Arrays.asList(0123456789);  
  4.         long c = list.stream().filter(i - > (i % 2 == 0)) //Intermediate method  
  5.             .count(); //Terminating method  
  6.         System.out.println(c);  
  7.     }  
  8. }  
forEach()
 
Declaration : void forEach(Consumer<? super T> action)
 
Here, the action is a type of input argument to the function.
 
The terminal method forEach(), takes input arguments of consumer type and iterates internally through the stream and prints the elements.
 
Example
  1. public class ForEachMethod {  
  2.     public static void main(String[] args) {  
  3.         List < Integer > list = Arrays.asList(0123456789);  
  4.         List < Integer > listnew = list.stream().filter(i - > (i % 2 == 0)) //Intermediate method  
  5.             .forEach(System.out::println); //Terminating method  
  6.     }  
  7. }  
reduce()
 
Declaration : T reduce(T identity, BinaryOperator<T> accumulator)
 
Here, identity is the value for accumulating function and the accumulator is an associative function for combining two values.
 
The terminal method reduce() is a general-purpose method for generating our custom reduction operations. The input argument identity is both the initial value of the reduction and default result if no element is present in the stream, the accumulator function takes two parameters: a partial result of the reduction and the next element of the stream.
 
Example
  1. public class ReduceMethod {  
  2.     public static void main(String[] args) {  
  3.         List < Integer > list = Arrays.asList(0123456789);  
  4.         int c = list.stream().reduce(0, Integer::sum) //Terminating method  
  5.         System.out.println(c);  
  6.     }  
  7. }  
max()
 
Declaration : Optional<T> max(Comparator<? super T> comparator)
 
The terminal method max() accepts a Comparator as an input argument and returns an Optional instance containing the maximum element of the stream or an empty Optional according to the Comparator. We use the get() method from optional class to fetch the value.
 
Example
  1. public class MaxMethod {  
  2.     public static void main(String[] args) {  
  3.         List < Integer > list = Arrays.asList(855126542758096105);  
  4.         int c = list.stream().max((a, b) - > a > b ? 1 : -1).get();  
  5.         System.out.println(c);  
  6.     }  
  7. }  
min()
 
Declaration : Optional<T> min(Comparator<? super T> comparator)
 
The terminal method min() accepts a Comparator as an input argument and returns an Optional instance containing the maximum element of the stream or an empty Optional according to the Comparator. We use the get() method from optional class to fetch the value.
 
Example
  1. public class MinMethod {  
  2.     public static void main(String[] args) {  
  3.         List < Integer > list = Arrays.asList(855126542758096105);  
  4.         int c = list.stream().min((a, b) - > a > b ? 1 : -1).get();  
  5.         System.out.println(c);  
  6.     }  
  7. }  

What did we learn?

 
After reading this article, users have a thorough knowledge of what Stream API and Stream are and their usage with existing Java versions.
 
Note
While trying to work with any feature of Java 8, remember to use JDK 1.8 or higher else these concepts and code will not work.
 

Summary

 
As discussed above, we have learned the following:
  • What is the Stream API and Stream interface?
  • How to obtain a stream?
  • Methods in the stream.
  • Intermediate methods and Terminal methods


Similar Articles