1. Overview
In this tutorial, We'll be talking about Spliterator introduced in the new Java 8 (JDK8). Spliterator is an interface that is designed for bulk or huge datasets and provides the best performance.Spliterator takes the data from the source and traverses it. Finally, it divides the data into partitions. Source data can be array, any collection or IO Channel such as files.
Spliterator does the partition using trySplit() method as another Spliterator. By partitioning, Operations can be performed parallel.
A Spliterator also reports a set of characteristics() of its structure, source, and elements from among ORDERED, DISTINCT, SORTED, SIZED, NONNULL, IMMUTABLE, CONCURRENT, and SUBSIZED. These may be employed by Spliterator clients to control, specialize or simplify computation.
For example, a Spliterator for a Collection would report SIZED, a Spliterator for a Set would report DISTINCT, and a Spliterator for a SortedSet would also report SORTED.
We will see its syntax and how Spliterator can be instantiated?
This has many useful methods and explanations for each method with example programs.
2. Spliterator Static Fields
This iterator has constants that are declared as static and final in the Spliterator interface. Below is the list of all constants.
public static final int ORDERED = 0x00000010; public static final int DISTINCT = 0x00000001; public static final int SORTED = 0x00000004; public static final int SIZED = 0x00000040; public static final int NONNULL = 0x00000100; public static final int IMMUTABLE = 0x00000400; public static final int CONCURRENT = 0x00001000; public static final int SUBSIZED = 0x00004000;
Each of these has its own character.
SIZED – if it's capable of returning an exact number of elements with the estimateSize() method SORTED – if it's iterating through a sorted source SUBSIZED – if we split the instance using a trySplit() method and obtain Spliterators that are SIZED as well CONCURRENT – if source can be safely modified concurrently DISTINCT – if for each pair of encountered elements x, y, !x.equals(y) IMMUTABLE – if elements held by source can't be structurally modified NONNULL – if source holds nulls or not ORDERED – if iterating over an ordered sequence
3. Spliterator Nested Classes
The following are the nested interfaces that are declared as static. To improve the performance, Spliterator has specialized implementations for Int, Double, Long and primitive types.
interface Spliterator.OfDouble A Spliterator specialized for double values. interface Spliterator.OfInt A Spliterator specialized for int values. interface Spliterator.OfLong A Spliterator specialized for long values. interface Spliterator.OfPrimitive<T,T_CONS,T_SPLITR extends Spliterator.OfPrimitive<T,T_CONS,T_SPLITR>> A Spliterator specialized for primitive values.
4. Spliterator Methods
This interface has a total of 8 methods. Among them, 4 are default methods that have implementation already in its interface.
Default Methods:
forEachRemaining(Consumer<? super T> action) getComparator() getExactSizeIfKnown() hasCharacteristics(int characteristics)
Abstract Methods:
characteristics() estimateSize() tryAdvance(Consumer<? super T> action) trySplit()
5. Spliterator Method Examples
Spliterator has a total of 8 methods. We will be seeing the example program on each method and how to use them properly.
First We'll start with default methods and next is to see one by one abstract method.
5.1 hasCharacteristics() Example
Syntax:default boolean hasCharacteristics(int characteristics);
Returns boolean if the provided character is supported by Spliterator for the underlying collection.
Example:
Below example, we will find each characteristic on ArrayList, HashSet, and TreeSet.
package com.java.w3schools.blog.java8.iterator.spliterator; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.Spliterator; import java.util.TreeSet; public class SpliteratorExample { public static void main(String[] args) { // Spliterator characteristics check for List ListOutput:lines = new ArrayList<>(); lines.add("This is line 1"); Spliterator listSpliterator = lines.spliterator(); System.out.println("ArrayList has the following characteristics : "); checkCharacteristics(listSpliterator); // Spliterator characteristics check for Set Set setLines = new HashSet (); setLines.add("This is line 1"); Spliterator setSpliterator = setLines.spliterator(); System.out.println("\nHashSet has the following characteristics : "); checkCharacteristics(setSpliterator); // Spliterator characteristics check for SortedSet Set sortedSetLines = new TreeSet (); sortedSetLines.add("This is line 1"); Spliterator sortedSetSpliterator = sortedSetLines.spliterator(); System.out.println("\nTreeSet has the following characteristics : "); checkCharacteristics(sortedSetSpliterator); } private static void checkCharacteristics(Spliterator spliterator) { if (spliterator.hasCharacteristics(Spliterator.CONCURRENT)) { System.out.println("CONCURRENT"); } if (spliterator.hasCharacteristics(Spliterator.DISTINCT)) { System.out.println("DISTINCT"); } if (spliterator.hasCharacteristics(Spliterator.IMMUTABLE)) { System.out.println("IMMUTABLE"); } if (spliterator.hasCharacteristics(Spliterator.NONNULL)) { System.out.println("NONNULL"); } if (spliterator.hasCharacteristics(Spliterator.ORDERED)) { System.out.println("ORDERED"); } if (spliterator.hasCharacteristics(Spliterator.SIZED)) { System.out.println("SIZED"); } if (spliterator.hasCharacteristics(Spliterator.SORTED)) { System.out.println("SORTED"); } if (spliterator.hasCharacteristics(Spliterator.SUBSIZED)) { System.out.println("SUBSIZED"); } } }
See the output for each collection characteristics are produced correctly by SplitIterator.
ArrayList has the following characteristics : ORDERED SIZED SUBSIZED HashSet has the following characteristics : DISTINCT SIZED TreeSet has the following characteristics : DISTINCT ORDERED SIZED SORTED
5.2 getExactSizeIfKnown() Example
getExactSizeIfKnown() method returns exact size for the collection is SIZED. Otherwise it returns -1 value.
Syntax:
default long getExactSizeIfKnown()
This is declared as a default method and returns long value for the count.
Internally this method calls estimatedSize() mehtod.
default long getExactSizeIfKnown() { return (characteristics() & SIZED) == 0 ? -1L : estimateSize(); }
Example:
import java.util.ArrayList; import java.util.List; import java.util.Spliterator; import java.util.stream.IntStream; /** * Spliterator getExactSizeIfKnown() Example * * @author Venkatesh * */ public class SpliteratorGetExactSizeIfKnownExample { public static void main(String[] args) { Listlines = new ArrayList<>(); IntStream.range(0, 100000).forEach(i -> lines.add("Line number " + i)); System.out.println("Original list size " + lines.size()); Spliterator linesIterator = lines.spliterator(); long estimatedSize = linesIterator.getExactSizeIfKnown(); System.out.println("lines spliterator count for getExactSizeIfKnown() : " + estimatedSize); Spliterator splittedLinesIterator = linesIterator.trySplit(); long splittedLinesIteratorCount = splittedLinesIterator.getExactSizeIfKnown(); System.out.println( "lines splittedLinesIteratorCount count for getExactSizeIfKnown() : " + splittedLinesIteratorCount); } }
Output:
Original list size 100000 lines spliterator count for getExactSizeIfKnown() : 100000 lines splittedLinesIteratorCount count for getExactSizeIfKnown() : 50000
This method works as same as estimatedSize() method.
5.3 forEachRemaining() Example
forEachRemaining() method takes action as an argument and performs the action on each element of the iterator. Action is performed in sequential order by the current thread if the collection is ORDERED. If any exception is thrown then it skips all remaining elements from the collection.
Note: Action is Consumer Functional Interface which has functional method accept(). If the specified action is null then it will throw NullPointerException
Syntax:
default void forEachRemaining(Consumer<? super T> action)
This is also a default method, just performs the given operation. It doesn't return any value.
Example:
Example:
import java.util.HashSet; import java.util.Set; import java.util.Spliterator; import java.util.stream.IntStream; /** * Spliterator forEachRemaining() Example * * @author Venkatesh * */ public class SpliteratoForEachRemainingExample { public static void main(String[] args) { Setlines = new HashSet<>(); IntStream.range(0, 10).forEach(i -> lines.add("Line number " + i)); System.out.println("Original list size " + lines.size()); // Main spliterator Spliterator linesIterator = lines.spliterator(); // Splitted spliterator Spliterator splittedLinesIterator = linesIterator.trySplit(); System.out.println("Elements processed by main iterator"); linesIterator.forEachRemaining(value -> System.out.println(value)); System.out.println("Elements processed by splitted iterator 1"); splittedLinesIterator.forEachRemaining(value -> System.out.println(value)); } }
Output:
Original list size 10 Elements processed by main iterator Line number 1 Line number 2 Line number 0 Elements processed by splitted iterator 1 Line number 9 Line number 7 Line number 8 Line number 5 Line number 6 Line number 3 Line number 4
5.4 getComparator() Example
getComparator() method returns Comparator that is used by SplitIterator source. If the SplitIterator's source is SORTED in the natural order, returns null.
Otherwise, if the source is not SORTED, throws IllegalStateException.
ArrayList is not sorted by default and if we call getComparator() then it throws IllegalStateException. Same for HashSet, LinkedHashSet, LinkedList which are not sorted.
Syntax:
default Comparator<? super T> getComparator()
This is also a default mehtod that returns Comparator that is used by the collection.
Example:
public class SpliteratoGetComparatorExample { public static void main(String[] args) { Set<String> lines = new HashSet<>(); lines.add("Line 1"); Spliterator<String> linesSpliterator = lines.spliterator(); Comparator<String> setComparator = (Comparator<String>) linesSpliterator.getComparator(); System.out.println(setComparator); } }
Output:
Because HashSet is not sorted and thrown IllegalStateException as expected.
Exception in thread "main" java.lang.IllegalStateException at java.base/java.util.Spliterator.getComparator(Spliterator.java:465) at com.java.w3schools.blog.java8.iterator.spliterator.SpliteratoGetComparatorExample.main(SpliteratoGetComparatorExample.java:23)
Now, We will see the example on TreeSet and what it returns.
Set<String> seasons = new TreeSet<>(); seasons.add("Summer"); seasons.add("Fall"); seasons.add("Snow"); Spliterator<String> seasonsSpliterator = seasons.spliterator(); Comparator<String> treeSetComparator = (Comparator<String>) seasonsSpliterator.getComparator(); System.out.println(seasons); System.out.println(treeSetComparator);
Output:
[Fall, Snow, Summer] null
Printed null because TreeSet is sorted in natural or ascending order.
Now make it to Desending order and it prints the compartor used for sorting.
Spliterator<String> reverseSpliterator = seasons2.spliterator(); Comparator<String> treeSetReverseComparator = (Comparator<String>) reverseSpliterator.getComparator(); System.out.println(seasons2); System.out.println(treeSetReverseComparator);
Output:
[Summer, Snow, Fall] java.util.Collections$ReverseComparator@4a574795
Note: IllegalStateException will be thrown if the spliterator does not report a characteristic of SORTED.
5.5 estimateSize() Example
estimateSize() method returns size of the underlying collection that would be processed by forEachRemaining() method. If the count is invalid or unknown or to expensive to compute then will return Long.MAX_VALUE( 2^263-1).
Sometimes size value may be differed if the Spliterator is split by using trySplit() method. The count must be lesser than its actual size which is determined by getExactSizeIfKnown() method.
Syntax:
long estimateSize()
Returns long value that holds the size of the spliterator.
Example:
Example program to get the estimate size for list spliterator and splitted spliterator count.
When we call trySplit() method it split the current iterator into two halfs. The first half by current iterator and second half are assigned to a new SplitIterator.
import java.util.ArrayList; import java.util.List; import java.util.Spliterator; import java.util.stream.IntStream; /** * Spliterator estimateSize() Example * * @author Venkatesh * */ public class SpliteratorEstimateSizeExample { public static void main(String[] args) { List<String> lines = new ArrayList<>(); IntStream.range(0, 100000).forEach(i -> lines.add("Line number " + i)); System.out.println("Original list size " + lines.size()); Spliterator<String> linesIterator = lines.spliterator(); long estimatedSize = linesIterator.estimateSize(); System.out.println("lines spliterator count for estimateSize() : " + estimatedSize); Spliterator<String> splittedLinesIterator = linesIterator.trySplit(); long splittedLinesIteratorCount = splittedLinesIterator.estimateSize(); System.out.println("lines splittedLinesIteratorCount count for estimateSize() : " + splittedLinesIteratorCount); } }
Output:
Original list size 100000 lines spliterator count for estimateSize() : 100000 lines splittedLinesIteratorCount count for estimateSize() : 50000
5.6 trySplit() Example
trySplit() method checks if this spliterator can be partitioned. If yes, it returns a new SplitIterator that covers a portion of elements from this SplitIterator.
Syntax:
Spliterator<T> trySplit()
Returns SplitIterator.
Example:
/** * Spliterator trySplit() Example * * @author Venkatesh * */ public class SpliteratoTrySplitExample { public static void main(String[] args) { Set<String> countires = new HashSet<>(); countires.add("USA"); countires.add("INDIA"); countires.add("UK"); countires.add("AUS"); countires.add("NZ"); countires.add("PAK"); countires.add("Canada"); Spliterator<String> countriesSpliterator = countires.spliterator(); Spliterator<String> splittedIterator = countriesSpliterator.trySplit(); System.out.println("countires list : " + countires); countriesSpliterator.forEachRemaining(value -> System.out.println(value)); System.out.println("-----------------"); splittedIterator.forEachRemaining(value -> System.out.println(value)); } }
Output:
countires list : [Canada, USA, UK, PAK, NZ, INDIA, AUS] PAK NZ INDIA AUS ----------------- Canada USA UK
If this Spliterator is ORDERED, the returned Spliterator must cover a strict prefix of the elements.
Set<String> countires = new TreeSet<>();
See the below output for the same above program. Just changed HashSet to TreeSet.
countires list : [AUS, Canada, INDIA, NZ, PAK, UK, USA] UK USA ----------------- AUS Canada INDIA NZ PAK
5.7 tryAdvance() Example
tryAdvance() method is used to get the next element from the collection if it is available. This takes Consumer as an argument and it just performs the action.
It checks logic hasNext() and next() operations in one method to reduce the boilerplate coding. Because of this, tryAdvance() method came into existance. And also if your collection is operated by multiple threads then tryAdvance() method ensure that if one thread gets value from iterator then the current thread will get the next value. This method is also designed to work in a multithreading enviornment.
It will be fetching the elements in sequential order if single thread is accessing.
Parallel processing is achieved by multipl threads.
Syntax:
boolean tryAdvance(Consumer<? super T> action)
This method takes Consumer Functional Interface and returns true if it has next element, false otherwise.
Single Thread tryAdvance() Example:
/** * Spliterator tryAdvance() Example * * @author Venkatesh * */ public class SpliteratoTryAdvanceExample { public static void main(String[] args) { // Set<String> countires = new TreeSet<>(); Set<String> states = new HashSet<>(); states.add("Alabama"); states.add("Hawaii"); states.add("Alaska"); states.add("Michigan"); states.add("Vermont"); states.add("Wisconsin"); states.add("Texas"); System.out.println("Current thread name: " + Thread.currentThread().getName()); System.out.println("States Set : " + states); Spliterator<String> statesSpliterator = states.spliterator(); int count = 0; while (statesSpliterator.tryAdvance(stateName -> System.out.println("US State Name : " + stateName))) { count++; } System.out.println("Total States : " + count); } }
Output:
Current thread name: main States Set : [Vermont, Texas, Hawaii, Alaska, Alabama, Wisconsin, Michigan] US State Name : Vermont US State Name : Texas US State Name : Hawaii US State Name : Alaska US State Name : Alabama US State Name : Wisconsin US State Name : Michigan Total States : 7
Multiple Threads Using tryAdvance() Example:
import java.util.ArrayList; import java.util.List; import java.util.Spliterator; /** * Spliterator tryAdvance() MultiThread Example * * @author Venkatesh * */ public class SpliteratoTryAdvanceMultiThreadExample { public static void main(String[] args) { // Set<String> countires = new TreeSet<>(); List<String> states = new ArrayList<>(); states.add("Alabama"); states.add("Hawaii"); states.add("Alaska"); states.add("Michigan"); states.add("Vermont"); states.add("Wisconsin"); states.add("Texas"); System.out.println("Main thread name: " + Thread.currentThread().getName()); System.out.println("States Set : " + states); Spliterator<String> statesSpliterator = states.spliterator(); // Thread 1 Thread thread1 = new Thread(new Runnable() { @Override public void run() { while (statesSpliterator.tryAdvance(state -> System.out .println("Thread Name: " + Thread.currentThread().getName() + ", State Name : " + state))) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Thread 1 done"); } }); // Thread 2 Thread thread2 = new Thread(new Runnable() { @Override public void run() { while (statesSpliterator.tryAdvance(state -> System.out .println("Thread Name: " + Thread.currentThread().getName() + ", State Name : " + state))) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Thread 2 done"); } }); thread1.start(); thread2.start(); System.out.println("Main thread done"); } }
Output:
See the output order of elements that processed by each thread. Thread 2 picked the elements from thread 1 already processed. We no need to write external synchronization.
Main thread name: main States Set : [Alabama, Hawaii, Alaska, Michigan, Vermont, Wisconsin, Texas] Main thread done Thread Name: Thread-0, State Name : Alabama Thread Name: Thread-1, State Name : Hawaii Thread Name: Thread-1, State Name : Alaska Thread Name: Thread-0, State Name : Michigan Thread Name: Thread-1, State Name : Vermont Thread Name: Thread-0, State Name : Wisconsin Thread Name: Thread-1, State Name : Texas Thread 1 done Thread 2 done
6. Custom SplitIterator
If we want to create our own or custon SplitIterator then our class must implement SplitIterator interface and must override the following abstract mehtods.
characteristics() estimateSize() tryAdvance(Consumer<? super T> action) trySplit()
Custom Spliterator Example:
/** * Custom Spliterator Example * * @author Venkatesh * */ public class CustomSplitIteratorExample { public static void main(String[] args) { } } class Customer { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
class CustomSpliterator implements Spliterator<Customer> { @Override public boolean tryAdvance(Consumer<? super Customer> action) { // Related custom logic return false; } @Override public Spliterator<Customer> trySplit() { // Related custom logic return null; } @Override public long estimateSize() { // Related custom logic return 0; } @Override public int characteristics() { // Related custom logic return 0; } }
7. Conclusion
In this article, We've covered Java 8 SplitIterator usage and its methods with working examples. SplitIterator provides a specialized way of processing primitive types.
And also written code to implement custom SplitIterator.
Shown examples are over GitHub
Java 8 Examples
No comments:
Post a Comment
Please do not add any spam links in the comments section.