java8的spliterator

Java8中容器在创建一个stream()流的过程中首先会创建一个相应的spliterator来作为迭代器来使用,这个迭代器支持并行迭代。

根据每个容器不同的性质,这个迭代器也对应了相应的特征量。

例如ArrayList。

public int characteristics() {
    return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}

其包含三个特征量,分别ordered表示该集合产生的迭代器的迭代顺序是按照原本容器中的顺序,sized表示该迭代器的大小是有限的,而subsized表示该迭代器所分割得到的子迭代器也是有序的。

又例如TreeMap中的spliterator迭代器KeySpliterator。

public int characteristics() {
    return (side == 0 ? Spliterator.SIZED : 0) |
        Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.ORDERED;
}

其包含的三个特征量distinct表示迭代器中数据存在唯一性,sorted表示其迭代器中的数据都经过相应的排序,而ordered则与ArrayList一致。

 

ArrayList中Spliaterator的实现是ArrayListSpliterator。

private final ArrayList list;
private int index; // current index, modified on advance/split
private int fence; // -1 until used; then one past last index
private int expectedModCount; // initialized when fence set

含有四个成员,list正是该迭代器所对应的数组,index则代表当前迭代器开始位置的下标,fence则代表当前结束位置的最后一个下标,而expectedModCount则存放了当该迭代器所对应的ArrayList的modCount来保证迭代器在迭代数据中原本数组中的数据并没有发生变化。

 

在ArrayListSpliterator中,当需要对需要迭代的数据进行分区的时候会调用trySplit()方法 ,将会根据当前的迭代器生成一个子迭代器。

private int getFence() { // initialize fence to size on first use
    int hi; // (a specialized variant appears in method forEach)
    ArrayList lst;
    if ((hi = fence) < 0) {
        if ((lst = list) == null)
            hi = fence = 0;
        else {
            expectedModCount = lst.modCount;
            hi = fence = lst.size;
        }
    }
    return hi;
}

public ArrayListSpliterator trySplit() {
    int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
    return (lo >= mid) ? null : // divide range in half unless too small
        new ArrayListSpliterator(list, lo, index = mid,
                                    expectedModCount);
}

在尝试从一个ArrayListSpliterator中通过分割获取一个子迭代器,首先会确定当前的迭代器的最后分隔下标,如果是-1,则代表此次是第一次使用,更新当前迭代器的expectedModCount为对应容器的modCount,同时更新fence为对应容器的size。

在得到相应的fence之后,将当前迭代器内容起始位置到 起始与结束位置的中间位置赋给新的子迭代器,同时当前迭代器的起始位置更新到中间位置,也就是子迭代器的结束位置上。这样完成一次分割。

 

关于迭代器中数据元素的遍历操作,给出了forEachRemaining()方法,这个方法的参数是一个Consumer,通过将迭代器中的数据依次交由Consumer中的函数进行调用,达到遍历的目的。

public void forEachRemaining(Consumer action) {
    int i, hi, mc; // hoist accesses and checks from loop
    ArrayList 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();
}

可以看到,这里是一次性对所有数据进行操作。最后对于数据元素的获取,还是回到了ArrayList当中,顺序也是依照原本数组当中的数据顺序。最后index会直接到最后的位置,代表此次迭代的完成。最后会对数组的modCount进行验证,保证迭代过程中数据并没有发生变化。

public boolean tryAdvance(Consumer 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;
}

tryAdvance()则是对下一个元素进行函数的调用,会即使更新到下一个下标,并且在这过程中每次都会对数组的ModCount进行验证。

通过上述可以看到,ArrayListSpliiterator的三个特征值,都得到了体现。

你可能感兴趣的:(jdk)