Java ArrayList spliterator() Example

Since Java 8, Spliterators are a special type of iterator that supports parallel iteration of portions of the source such as list, set, or array.

ArrayList

Since Java 8, Spliterators are a special type of iterator that supports parallel iteration of portions of the source such as an Array, Set, List, or IO channel. Although Spliterators mainly support parallel programming, we can use Spliterator even if we won’t be using parallel execution.

The ArrayList.spliterator() returns the instance of Spliterator that is last-binding and fail-fast.

  • Late binding means it binds to the data source at the point of first traversal or first split, rather than at the time of the Spliterator creation.
  • Fail-fast means that it throws a ConcurrentModificationException if it detects that the data source has been structurally modified after it was created. Structural changes include adding, removing, or updating elements, which can compromise the consistency of the data being traversed.
// Get the Spliterator from the ArrayList
Spliterator<Integer> spliterator = arraylist.spliterator();

// Convert Spliterator to parallel Stream and process elements in parallel
StreamSupport.stream(spliterator, true).forEach(System.out::println); 

1. ArrayList spliterator() Method

In the case of ArrayList, the spliterator() method returns the instance of the ArrayListSpliterator class which is in an inner class of ArrayList and implements Spliterator interface.

public Spliterator<E> spliterator() {
    return new ArrayListSpliterator<>(this, 0, -1, 0);
}
 
static final class ArrayListSpliterator<E> implements Spliterator<E> {
 
  //implementation
}

The spliterator returned by ArrayList is :

  • ORDERED – indicates that the elements have a defined order when traversing and partitioning.
  • SORTED – means that the elements follow a predefined sort order.
  • SUBSIZED – indicates that this and any further resulting Spliterator are SIZED. Here SIZED means the Spliterator has been created from a source with a known size.

2. ArrayList spliterator() Example

A Spliterator can be used for many usecases. A few are usecases are discussed in below given examples.

2.1. The tryAdvance(): Iterate one element at a time

Java program to iterate one element at a time using a spliterator. It is equivalent to iterator.next() method from Iterator interface.

ArrayList<Integer> digits = new ArrayList<>(Arrays.asList(1,2,3,4,5,6));

Spliterator<Integer> sItr = digits.spliterator();
 
sItr.tryAdvance( d -> System.out.println( d ) );
sItr.tryAdvance( d -> System.out.println( d ) );
sItr.tryAdvance( d -> System.out.println( d ) );

Program output.

1
2
3

2.2. The forEachRemaining(): Iterate all elements

Java program to iterate all elements and perform an action on them. It is equivalent to iterator.hasNext() method along with iterator.next() in a loop.

ArrayList<Integer> digits = new ArrayList<>(Arrays.asList(1,2,3,4,5,6));

Spliterator<Integer> sItr = digits.spliterator();
 
sItr.tryAdvance( d -> System.out.println( d ) ); //1
sItr.tryAdvance( d -> System.out.println( d ) ); //2
 
sItr.forEachRemaining( d -> System.out.println( d ) ); //3,4,5,6

Program output.

1
2
3
4
5
6

2.3. The trySplit(): Parallel Processing

If you are working on a concurrent application and the list has a large number of elements then it’s a good idea to divide the list into two parts and process parallelly.

The trySplit() method splits the current spliterator into two and returns the new one. The elements it is pointing to are divided into two equal lists.

Keep in mind that the individual Spliterator is not thread safe, by default. It is responsibility of application code to create different threads and hand over the Spliterator instances.

ArrayList<Integer> digits = new ArrayList<>(Arrays.asList(1,2,3,4,5,6));

Spliterator<Integer> sItr1 = digits.spliterator();
Spliterator<Integer> sItr2 = sItr1.trySplit();
 
System.out.println(sItr1.getExactSizeIfKnown());      //3
sItr1.forEachRemaining( d -> System.out.println( d ) );    //4,5,6
 
System.out.println("===========");
 
System.out.println(sItr2.getExactSizeIfKnown());      //3
sItr2.forEachRemaining( d -> System.out.println( d ) );    //1,2,3  

Program output.

3
4
5
6
===========
3
1
2
3

2.4. Batch Processing

The Spliterator can be used for bulk processing operations, such as applying a batch operation to a subset of elements.

import java.util.ArrayList;
import java.util.List;
import java.util.Spliterator;

public class BatchProcessingExample {

    public static void main(String[] args) {

        List<String> list = new ArrayList<>();
        list.add("One");
        list.add("Two");
        list.add("Three");
        list.add("Four");

        Spliterator<String> spliterator = list.spliterator();
        
        int batchSize = 2;
        List<String> batch = new ArrayList<>(batchSize);
        
        while (spliterator.tryAdvance(batch::add)) {
            if (batch.size() == batchSize) {
                processBatch(batch);
                batch.clear();
            }
        }
        
        if (!batch.isEmpty()) {
            processBatch(batch);
        }
    }

    private static void processBatch(List<String> batch) {
        System.out.println("Processing batch: " + batch);
    }
}

2.5. Splitting Workload for Recursive Algorithms

The Spliterator can be split and used in recursive algorithms, such as implementing a parallel version of a divide-and-conquer algorithm.

import java.util.ArrayList;
import java.util.List;
import java.util.Spliterator;

public class RecursiveSplittingExample {

    public static void main(String[] args) {

        List<Integer> list = new ArrayList<>();
        for (int i = 1; i <= 16; i++) {
            list.add(i);
        }

        Spliterator<Integer> spliterator = list.spliterator();
        parallelSum(spliterator);
    }

    private static void parallelSum(Spliterator<Integer> spliterator) {
        long size = spliterator.estimateSize();
        if (size <= 4) { // Base case for the recursive splitting
            spliterator.forEachRemaining(element -> System.out.println(Thread.currentThread().getName() + " - " + element));
            return;
        }

        Spliterator<Integer> split = spliterator.trySplit();
        if (split != null) {
            Thread leftThread = new Thread(() -> parallelSum(spliterator));
            Thread rightThread = new Thread(() -> parallelSum(split));
            leftThread.start();
            rightThread.start();
            try {
                leftThread.join();
                rightThread.join();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

3. Difference between Iterator and Spliterator

IteratorSpliterator
Since Java 1.2.Since Java 8.
Can be used to iterate all collection classes.Can be used to iterate array, stream, list and set. Not possible with map.
Does not support parallel processing.Supports parallel processing.

That’s all for the ArrayList spliterator() method in Java.

Happy Learning !!

Read More:

A Guide to Java ArrayList
ArrayList Java Docs
Spliterator Java Docs

Weekly Newsletter

Stay Up-to-Date with Our Weekly Updates. Right into Your Inbox.

Comments

Subscribe
Notify of
0 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.