java源码阅读之Spliterator

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);
            }
        });
    }
}

结果

这里写图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值