Java 8 Stream API

Reading Time: 5 minutes
Java 8 Stream API

Stream

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.

A Stream can be obtained in various ways such as

  • By calling stream() and parallelStream() 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.

Example:- 

String[] arr = new String[]{“a”, “b”, “c”};

Stream<String> streamOfArrayFull = Arrays.stream(arr);

Stream<String> streamOfArrayPart = Arrays.stream(arr, 1, 3);

Stream Operation

Stream operations are divided into 2 types

  1. Intermediate Operation
  2. Terminal Operation

Intermediate Operation

Intermediate operations will be located everywhere in the stream except at the end. They return a stream object and does not execute any operation in the pipeline.

  1. 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.

Example:-

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.

Output:- [4, 10, 14, 18, 20, 40, 60, 80]

  1. filter() operation is used to select the elements that match the given predicate.

Example:-

       List <String> nameList = Arrays.asList(“Ankit”, “Amit”,”Vijay”,);

       List<String> filteredList = nameList

.stream()

.filter(name -> name.startsWith(“A”)) .collect(Collectors.toList());

The above block of code will create a new list that contains the names that start with ‘A’.

Output:-  [Ankit, Amit]

3. sorted() operation as the name suggests, can be used to sort a stream.

Example:- 

       List <String> nameList = Arrays.asList(“Ravi”, “Amit”, “Bhavesh”);

       List<String> sortedList = nameList

.stream()

.sorted()

.collect(Collectors.toList());

Calling the sorted operation on the nameList after converting into a    

         The stream will sort the names in their natural order.

Output:- [Amit, Bhavesh, Ravi]

Terminal Operation

Terminal operations will be located only at the end of the stream. They execute the pipeline. They do not return stream objects so no other Intermediate operations or terminal operations can be added after them.

  1. 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.
  1. 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.

Example:-

        List <String> nameList = Arrays.asList(“Amit”, “Ravi”, “Nisha”);

        Boolean isBenExist = nameList

         .stream()

         .anyMatch(name -> name.equals(“Amit”));

The above operation will return True since ‘Amit’ is available in the nameList.

  1. findFirst() operation ideally returns the first element in a stream as an Optional type.

Example:-

            List <String> nameList = Arrays.asList(“Amit”, “Ram”, “Ankit”);

Optional<String> firstElement = nameList

         .stream()

        .filter(name -> name.startsWith(“A”))

        .findFirst();

System.out.println(firstElement.isPresent() ? firstElement.get() :  “Not Found”);

The above operations will filter the stream and find the elements that start with ‘B’ and then the findFirst() will return the first element of the filtered stream.

Output:- [Amit]

Some Important notes

A stream will take each element in the source collection and will send it throughout all the operations in the pipeline before dealing with the next element in the source collection. Well, that’s a lie, it only behaves like that if all the operations in the pipeline are stateless.

Let’s view the order of a calculation for each element in the source control incase all the operations in the pipeline are stateless:

Arrays.asList(1,2,3).stream()

        .filter((Integer x) -> x > 1)

        .map((Integer x) -> x * 10)

        .forEach(System.out::println);

source collection: 1, 2 ,3

filter(1) -> You are not OK. Element 1 will not pass to the next operation in the pipeline. Now deal with element 2.

filter(2) -> You are OK. element 2 passes to the next operation.

map(2) -> create new element 20 and put it in the new stream.

forEach(20) -> print 20. End dealing with element 2 in the source collection.

Now deal with element 3.

filter(3) -> You are OK. element 3 passes to the next operation

map(3) -> create new element 30 and put it in the new stream.

forEach(20) -> print 30. No more elements in the source collection.

finish executing the stream.

Output:-  20

       30

2. Stream<T> sorted()

The sorted method is an intermediate operation and it will create a new stream that all its elements are sorted according to the natural order. T must implement Comparable interface so you can override int Comparable.compareTo(T o). The sorted method can’t send every element it gets to the next operation in the pipeline because the order of the elements is matters and there is a chance that sorted will receive element in the future with smaller value so this element should be sent first to the next operation in the pipeline. sorted use a temporary collection to store all the elements it gets and it starts sorting them from the time it gets executed.

Example:-

Arrays.asList(1,2,3,1,4).stream()

        .filter((Integer x) -> x > 1)

        .sorted()

        .forEach(System.out::println);

Output:- 2

      3

               4

3. Stream<T> limit(long maxSize)

The limit method is an intermediate operation that will create a new stream that contains at most n elements where n=maxSize. When n elements passed him or the last operation can’t give more to limit, the limit will end the execution of the all the stream. To answer the question if this operation is stateful or not, we need to understand who are those elements that limit let them pass to the next operation in the pipeline. For now, let’s think that this operation is stateless and limit lets the first n elements that it gets to pass at most.

Example:-

Arrays.asList(1,2,3,4,5).stream()

         .limit(2)

         .filter((Integer x)-> x>1)

         .forEach(System.out::println);

Output:- 2

References:-

knoldus-advt-sticker