Spliterator是java 8才开始提供的一个迭代器实现,从名称可以看出来,Spliterator是一个可分割的迭代器,用来分割和迭代给定源的元素,这里的源可以是collection,array和io等。
spliterator的特别之处在于,它可以通过trySplit()
方法,将spliterator实例分割成多个较小的spliterator实例,方便多线程并发处理。
spliterator还可以通过方法
int characteristics();
返回当前实例的一个特征集,这个特征值集包括:
//表示迭代器需要按照其原始顺序迭代其中元素的
public static final int ORDERED = 0x00000010;
//迭代器中的元素是没有重复的
public static final int DISTINCT = 0x00000001;
//迭代器是按照某种方式排序的顺序迭代其中元素的
public static final int SORTED = 0x00000004;
//表示迭代器将要迭代的元素的个数是可计数的,有界的
public static final int SIZED = 0x00000040;
//迭代器迭代的元素是没有值为`null`的
public static final int NONNULL = 0x00000100;
//迭代器迭代的元素是不可改变的,也不可以增加、替换和删除
public static final int IMMUTABLE = 0x00000400;
//表示迭代器的数据源是线程安全的
public static final int CONCURRENT = 0x00001000;
//表示当前迭代器所有的子迭代器(直接的或者间接的),都是`SIZED`和`SUBSIZED`的
public static final int SUBSIZED = 0x00004000;
例如一个Collection实例的spliterator会报告一个SIZED
的特征,一个Set实例会报告一个DISTINCT
特征,等。
如果一个迭代器没有报告IMMUTABLE
或者CONCURRENT
特征时,其绑定数据源后会检查数据源的结构化改动。后绑定的迭代器是绑定数据源发生在其第一次遍历、第一次拆分、第一次查询大小时,而不是在迭代器创建后立刻绑定数据源。和其它迭代器一样,绑定前对源的修改可以反应在迭代器遍历过程中,而绑定后的对源的修改会导致ConcurrentModificationException
异常。
Spliterator的批量遍历方法(forEachRemaining()
)可以遍历所有元素之后优化遍历并检对源的结构化修改,而不是逐个检查元素并立即抛出失败。
Spliterator提供了estimateSize()
方法用来估算迭代器中剩余元素的数量,理想情况下,它能够准确的计算迭代器中能够成功迭代的元素的数量,即使是在不准切的时候,其结果依然能够为对迭代器的下一步操作,如是直接迭代剩余元素还是继续分割迭代器提供依据。
尽管Spliterator为并发迭代提供了入口,但它依旧是线程不安全的。相反,spliterator的并发实现必须保证其在同一个时刻只有一个线程访问。调用trySplit()
的线程可能会后续将迭代器交给其他线程进行迭代或者继续分割,并发的分割或者迭代同一个迭代器会导致不确定的结果。如果需要将迭代器由一个线程递交给另一个线程,需要保证递交前没有使用tryAdvance()
消费任何元素。
Spliterator为原始类型中的int、long、double提供了专门的子类并默认实现了tryAdvance(java.util.function.Consumer)
和forEachRemaining(java.util.function.Consumer)
。
方法
estimateSize()
估计当前Spliterator实例中将要迭代的元素的数量,如果数量是无限的、未知的或者计算数量的花销太大,则返回Long.MAX_VALUE
trySplit()
分割迭代器,没调用一次,将原来的迭代器等分为两份,并返回索引靠前的那一个子迭代器。
forEachRemaining(Consumer<? super E> action)
通过action
批量消费所有的未迭代的数据。
实现样例
以ArrayListSpliterator为例说明Spliterator的实现:
static final class ArrayListSpliterator<E> implements Spliterator<E> {
//ArrayList数据源的对象引用
private final ArrayList<E> list;
private int index; // 起始位置所有,会被advance()和split()函数修改。
private int fence; // 结束位置,-1表示到最后一个元素
private int expectedModCount; // initialized when fence set
ArrayListSpliterator(ArrayList<E> list, int origin, int fence, int expectedModCount) {
this.list = list; // OK if null unless traversed
this.index = origin;
this.fence = fence;
this.expectedModCount = expectedModCount;
}
private int getFence() { // initialize fence to size on first use
int hi; // (a specialized variant appears in method forEach)
ArrayList<E> lst;
if ((hi = fence) < 0) {
if ((lst = list) == null)
hi = fence = 0;
else {
expectedModCount = lst.modCount;
hi = fence = lst.size;
}
}
return hi;
}
// 每次分割,都将原来的迭代器等分为两个,并返回索引靠前的那个
public ArrayListSpliterator<E> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid) ? null : new ArrayListSpliterator<E>(list, lo, index = mid, expectedModCount); // divide range in half unless too small
}
// 使用action消费下一个元素
public boolean tryAdvance(Consumer<? super E> action) {
if (action == null)
throw new NullPointerException();
int hi = getFence(), i = index;
if (i < hi) {
index = i + 1;
@SuppressWarnings("unchecked")
E e = (E)list.elementData[i];
action.accept(e);
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
return false;
}
// 批量消费所有未迭代的元素
public void forEachRemaining(Consumer<? super E> action) {
int i, hi, mc; // hoist accesses and checks from loop
ArrayList<E> lst; Object[] a;
if (action == null)
throw new NullPointerException();
if ((lst = list) != null && (a = lst.elementData) != null) {
if ((hi = fence) < 0) {
mc = lst.modCount;
hi = lst.size;
} else
mc = expectedModCount;
if ((i = index) >= 0 && (index = hi) <= a.length) {
for (; i < hi; ++i) {
@SuppressWarnings("unchecked") E e = (E) a[i];
action.accept(e);
}
if (lst.modCount == mc)
return;
}
}
throw new ConcurrentModificationException();
}
//估算将要迭代的元素的数量
public long estimateSize() {
return (long) (getFence() - index);
}
//特征值,对于ArrayListSpliterator,其迭代顺序是按照原始顺序迭代的,而且可以预估数量。
public int characteristics() {
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}
此外,在Spliterators类中,默认为int、long、double等几种基本类型的集合实现了其Spliterator迭代器。
使用
package com.company;
import java.util.ArrayList;
import java.util.Spliterator;
import java.util.function.Consumer;
public class IteratorTest {
public static void main(String[] args) {
ArrayList<String> arrays = new ArrayList<String>();
arrays.add("a");
arrays.add("b");
arrays.add("c");
arrays.add("d");
arrays.add("e");
arrays.add("f");
arrays.add("g");
arrays.add("h");
arrays.add("i");
arrays.add("j");
arrays.remove("j");
Spliterator<String> p = arrays.spliterator();
Spliterator<String> s1 = p.trySplit();
Spliterator<String> s2 = p.trySplit();
System.out.println("p.consume :");
p.forEachRemaining(new Consumer<String>() {
public void accept(String s) {
System.out.println(s);
}
});
System.out.println("s1.consume");
s1.forEachRemaining(new Consumer<String>() {
public void accept(String s) {
System.out.println(s);
}
});
System.out.println("s2.consume");
s2.forEachRemaining(new Consumer<String>() {
public void accept(String s) {
System.out.println(s);
}
});
}
}
结果