Chapter: Java The Complete Reference - The Java Library - The Stream API

| Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail |

Iterators and Streams

Although a stream is not a data storage object, you can still use an iterator to cycle through its elements in much the same way as you would use an iterator to cycle through the elements of a collection.

Iterators and Streams

 

Although a stream is not a data storage object, you can still use an iterator to cycle through its elements in much the same way as you would use an iterator to cycle through the elements of a collection. The stream API supports two types of iterators. The first is the traditional Iterator. The second is Spliterator, which was added by JDK 8. It provides significant advantages in certain situations when used with parallel streams.

 

Use an Iterator with a Stream

 

As just mentioned, you can use an iterator with a stream in just the same way that you do with a collection. Iterators are discussed in Chapter 18, but a brief review will be useful here. Iterators are objects that implement the Iterator interface declared in java.util. Its two key methods are hasNext( ) and next( ). If there is another element to iterate, hasNext( ) returns true, and false otherwise. The next( ) method returns the next element in the iteration.

 

To obtain an iterator to a stream, call iterator( ) on the stream. The version used by Stream is shown here.

 

Iterator<T> iterator( )

 

Here, T specifies the element type. (The primitive streams return iterators of the appropriate primitive type.)

The following program shows how to iterate through the elements of a stream. In this case, the strings in an ArrayList are iterated, but the process is the same for any type of stream.

 

// Use an iterator with a stream.

 

import java.util.*;

 

import java.util.stream.*;

 

class StreamDemo8 {

 

public static void main(String[] args) {

 

     //Create a list of Strings.

     ArrayList<String> myList = new ArrayList<>( ); myList.add("Alpha");

 

myList.add("Beta");

 

myList.add("Gamma");

 

myList.add("Delta");

 

myList.add("Phi");

 

myList.add("Omega");

 

//Obtain a Stream to the array list.

Stream<String> myStream = myList.stream();

 

     //Obtain an iterator to the stream. I

     terator<String> itr = myStream.iterator();

 

     //Iterate the elements in the stream.

 

     while(itr.hasNext())

 

System.out.println(itr.next());

 

}

 

}

 

The output is shown here:

 

Alpha

 

Beta

 

Gamma

 

Delta

 

Phi

 

Omega

 

Use Spliterator

 

Spliterator offers an alternative to Iterator, especially when parallel processing is involved. In general, Spliterator is more sophisticated than Iterator, and a discussion of Spliterator is found in Chapter 18. However, it will be useful to review its key features here. Spliterator defines several methods, but we only need to use three. The first is tryAdvance( ). It performs an action on the next element and then advances the iterator. It is shown here:

 

boolean tryAdvance(Consumer<? super T> action)

 

Here, action specifies the action that is executed on the next element in the iteration. tryAdvance( ) returns true if there is a next element. It returns false if no elements remain. As discussed earlier in this chapter, Consumer declares one method called accept( ) that receives an element of type T as an argument and returns void.

 

Because tryAdvance( ) returns false when there are no more elements to process, it makes the iteration loop construct very simple, for example:

 

while(splitItr.tryAdvance( // perform action here );

 

As long as tryAdvance( ) returns true, the action is applied to the next element. When tryAdvance( ) returns false, the iteration is complete. Notice how tryAdvance( ) consolidates the purposes of hasNext( ) and next( ) provided by Iterator into a single method. This improves the efficiency of the iteration process.

The following version of the preceding program substitutes a Spliterator for the Iterator:

 

// Use a Spliterator.

 

import java.util.*;

 

import java.util.stream.*;

 

class StreamDemo9 {

 

public static void main(String[] args) {

 

     //Create a list of Strings.

     ArrayList<String> myList = new ArrayList<>( ); myList.add("Alpha");

 

myList.add("Beta");

 

myList.add("Gamma");

 

myList.add("Delta");

 

myList.add("Phi");

 

myList.add("Omega");

 

     //Obtain a Stream to the array list.

     Stream<String> myStream = myList.stream();

 

     //Obtain a Spliterator.

 

Spliterator<String> splitItr = myStream.spliterator();

 

// Iterate the elements of the stream.

while(splitItr.tryAdvance((n) -> System.out.println(n)));

 

}

 

}

 

The output is the same as before.

 

In some cases, you might want to perform some action on each element collectively, rather than one at a time. To handle this type of situation, Spliterator provides the forEachRemaining( ) method, shown here:

 

default void forEachRemaining(Consumer<? super T> action)

 

This method applies action to each unprocessed element and then returns. For example, assuming the preceding program, the following displays the strings remaining in the stream:

 

splitItr.forEachRemaining((n) -> System.out.println(n));

 

Notice how this method eliminates the need to provide a loop to cycle through the elements one at a time. This is another advantage of Spliterator.

 

One other Spliterator method of particular interest is trySplit( ). It splits the elements being iterated in two, returning a new Spliterator to one of the partitions. The other partition remains accessible by the original Spliterator. It is shown here:

 

Spliterator<T> trySplit( )

 

If it is not possible to split the invoking Spliterator, null is returned. Otherwise, a reference to the partition is returned. For example, here is another version of the preceding program that demonstrates trySplit( ):

 

// Demonstrate trySplit().

 

import java.util.*;

 

import java.util.stream.*;

 

class StreamDemo10 {

 

public static void main(String[] args) {

 

     //Create a list of Strings.

     ArrayList<String> myList = new ArrayList<>( ); myList.add("Alpha");

 

myList.add("Beta");

 

myList.add("Gamma");

 

myList.add("Delta");

 

myList.add("Phi");

 

myList.add("Omega");

 

     //Obtain a Stream to the array list.

     Stream<String> myStream = myList.stream();

 

     //Obtain a Spliterator.

 

Spliterator<String> splitItr = myStream.spliterator();

 

     //Now, split the first iterator.

     Spliterator<String> splitItr2 = splitItr.trySplit();

 

     //If splitItr could be split, use splitItr2 first.

     if(splitItr2 != null) {

 

System.out.println("Output from splitItr2: ");

splitItr2.forEachRemaining((n) -> System.out.println(n));

}

 

     //Now, use the splitItr.

 

System.out.println("\nOutput from splitItr: "); splitItr.forEachRemaining((n) -> System.out.println(n));

 

}

 

}

The output is shown here:

 

Output from splitItr2:

 

Alpha

 

Beta

 

Gamma

 

Output from splitItr:

 

Delta

 

Phi

 

Omega

 

Although splitting the Spliterator in this simple illustration is of no practical value, splitting can be of great value when parallel processing over large data sets. However, in many cases, it is better to use one of the other Stream methods in conjunction with a parallel stream, rather than manually handling these details with Spliterator. Spliterator is primarily for the cases in which none of the predefined methods seems appropriate.



More to Explore in the Stream API

 

This chapter has discussed several key aspects of the stream API and introduced the techniques required to use them, but the stream API has much more to offer. To begin, here are a few of the other methods provided by Stream that you will find helpful:

 

        To determine if one or more elements in a stream satisfy a specified predicate, use allMatch( ), anyMatch( ), or noneMatch( ).

 

        To obtain the number of elements in the stream, call count( ).

 

        To obtain a stream that contains only unique elements, use distinct( ).

 

        To create a stream that contains a specified set of elements, use of( ).

 

One last point: the stream API is a powerful addition to Java. It is likely that it will be enhanced over time to include even more functionality. Therefore, a periodic perusal of its API documentation is advised.

 

Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail


Copyright © 2018-2020 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.