Using Spliterator In Java
Introduction:
Iterators in Java are used to traverse elements of a given source. Spliterator in Java is one among the four available Java Iterators – Iterator, Enumeration, ListIterator, and Spliterator. It is an interface available in java.util package.
Spliterator was first introduced in Java 8 to support parallel programming. However, we can use it for both sequential and parallel processing of data items. To obtain an instance of Java Spliterator, we’ll use the spliterator() method:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); Spliterator splitr = list.spliterator();
We can think of Java Spliterator as:
Spliterator = Splitting + Iteration
Spliterator Characteristics:
A Spliterator interface defines some integral constants representing its characteristics. Our instance can have one or more of the below eight characteristics:
- SIZED – capable of returning the exact number of elements in the source when we invoke estimateSize() method
- SUBSIZED – When we split the instance using trySplit() and obtain SIZED SplitIterators as well
- ORDERED – iterating over an ordered sequence
- SORTED – iterating over a sorted sequence
- NONNULL – source guarantees to have not null values
- DISTINCT – no duplicates exist in our source sequence
- IMMUTABLE – if we can’t structurally modify the element source
- CONCURRENT – the element source can be safely concurrently modified
We can use int characteristics() method to query the characteristics of our Spliterator instance. It returns an OR’ed value of all of the qualifying characteristic values for our Spliterator. For our defined splitr, we’ll have:
int charactersticsORed = splitr.characteristics(); //16464
hasCharacteristics():
We can use boolean hasCharacteristics(int characteristic) method to check if our instance has a given characteristic or not:
boolean isSized = splitr.hasCharacteristics(Spliterator.SIZED); //true boolean isSorted = splitr.hasCharacteristics(Spliterator.SORTED); //false boolean isNonNull = splitr.hasCharacteristics(Spliterator.NONNULL); //false
estimateSize():
The estimateSize() method returns an estimated number of elements left to iterate over. It returns Long.MAX_VALUE if the value is infinite, unknown or too expensive to compute. For a SIZED Spliterator, it returns a value which exactly corresponds to the number of elements that would be encountered in a successful traversal:
long estimatedSize = splitr.estimateSize(); // 5
getExactSizeIfKnown():
It’s just a convenience method which returns estimateSize() if it’s a SIZED Spliterator or else returns -1:
long size = splitr.getExactSizeIfKnown(); // 5
tryAdvance():
The signature of tryAdvance() method looks like:
default boolean tryAdvance(Consumer<? super T> action)
The tryAdvance() method in Spliterator combines the hasNext() and next() operators present in a basic Iterator. So if a remaining element exists, it performs a given action on it, returning true; else returns false. In other words, it performs an action on the next element in the sequence and then advances the iterator.
while(splitr.tryAdvance((item) -> System.out.println(item)));
If we have an ORDERED Spliterator, the action is performed on the next element in the encounter order.
forEachRemaining():
The forEachRemaining(Consumer<? superT> action) method performs the given action for each remaining element, sequentially in the current thread, until all elements have been processed or the action throws an exception:
splitr.forEachRemaining(item -> System.out.println(item));
The current default implementation repeatedly invokes tryAdvance() until it returns false.
trySplit():
If partitioning is possible, trySplit() method splits the invoking Spliterator and returns a reference to the Spliterator covering elements which won’t be covered by this Spliterator upon return from this method. Otherwise, it returns null.
So after a successful split, the original Spliterator will iterate over the one portion of the sequence and the returned Spliterator over the other portion of it.
Also, the returned Spliterator covers a strict prefix of the elements for an initial ORDERED Spliterator (Eg: over a List):
// trySplit() method over ORDERED splitr Spliterator<Integer> splitrNew = splitr.trySplit(); // Elements in our splitrNew = {1, 2, 3} if(splitrNew != null) { splitrNew.forEachRemaining((n) -> System.out.println(n)); } // Elements in our splitr - {4 , 5} splitr.forEachRemaining((n) -> System.out.println(n));
Unless our original Spliterator represents an infinite sequence, repeated calls to trySplit() must eventually return null.
getComparator():
If we have a Spliterator SORTED by a Comparator, it returns that Comparator. Or else it returns null If the source is sorted in a natural order. For a source that isn’t SORTED, it will throw an IllegalStateException.
So for our example, we have:
Comparator<Integer> comparator = splitr.getComparator(); //throws IllegalStateException
Why use Spliterator?
Java Spliterator offers us several advantages:
- Supports parallel programming
- We can use it for both sequential and parallel processing of data items
- tryAdvance() method combines both next() and hasNext() operations of a simple Iterator and so offers a better performance
Also, it’s important to realize that the Spliterator works fine for both Collection and Stream sources, but not with the Map implementations as the source.
Conclusion:
In this article, we introduced you to Spliterator interface in Java. We covered different default methods available in this interface and how to use them.
Be the First to comment.
Published on Java Code Geeks with permission by Shubhra Srivastava, partner at our JCG program. See the original article here: Using Spliterator In Java Opinions expressed by Java Code Geeks contributors are their own. |