Spliterator

The Iterable interface includes a default method named spliterator that returns a Spliterator object.  The Spliterator interface provides useful methods for applying some action to each of the element in the collection and partitioning a collection for use in parallel processing algorithms.

The Spliterator interface has the following methods:

  • default void forEachRemaining(Consumer<? super T> action)
  • boolean tryAdvance(Consumer<? super T>  action)
  • Spliterator<T> trySplit()
  • long estimateSize()
  • int characteristics()
  • default boolean hasCharacteristics(int val)
  • default Comparator<? super T> getComparator()

For this course we’re interested in the first two methods because their arguments are functional interfaces.  But before we continue and discuss these methods, we should note that arbitrary and non-deterministic behavior can occur if structural interference (elements added, replaced or removed) occurs on the data source between the time that the Spliterator binds to the data source and the end of traversal.

forEachRemaining

The forEachRemaining method applies a functional interface method to each element in the data set that has yet to be traversed.  The  argument, an instance of the Consumer interface’s must have the following signature:

void accept(T objRef)

Below is an example of how we can get a Spliterator for an instance of ArrayList and use a lambda expression and functional reference to implement the Consumer when calling the Spliterator’s forEachRemaining method.

ArrayList<Student> list = new ArrayList<>();
list.add(new Student("Alice"));
list.add(new Student("Bob"));
list.add(new Student("Cece"));

Spliterator<Student> sp = list.spliterator();
sp.forEachRemaining((s) -> { s.name = s.name.toUpperCase(); });

list.spliterator().forEachRemaining(Student::print);

Below is another example that creates uses forEachRemaining to create a clone of an ArrayList.

ArrayList<Integer> list2 = new ArrayList<>();
ArrayList<Integer> list3 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);

list2.spliterator().forEachRemaining((x) -> { list3.add(x); });

list3.spliterator().forEachRemaining((i) -> {
    System.out.printf(i + " ");
});

tryAdvance

The tryAdvance method also takes an instance of the Consumer functional interface as an argument.  If there is a next element to traverse, it traverses the element, applies the Consumer’s method to the element and returns true, otherwise it returns false.

ArrayList<Student> list = new ArrayList<>(); 
list.add(new Student("Alice")); 
list.add(new Student("Bob")); 
list.add(new Student("Cece"));

sp = list.spliterator();
while(sp.tryAdvance((s) -> {
    s.name = "Jane";
}));

list.spliterator().forEachRemaining(Student::print);

© 2017 – 2019, Eric. All rights reserved.