java 数据结构之arraylist Spliterator 使用和源码解析

Posted xiaoliuliu2050

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java 数据结构之arraylist Spliterator 使用和源码解析相关的知识,希望对你有一定的参考价值。

以下是一个案例来使用spliterator :

首先需要定义一个线程,然后需要将拆分后的迭代器传入该线程。执行start()方法让实现的run()方法进入就绪状态等待调用

自定义线程代码如下:

 1 package com.FM.ArrayListStudy;
 2 
 3 import java.util.Spliterator;
 4 import java.util.function.Consumer;
 5 
 6 /**
 7  * 内部类,线程调用
 8  */
 9 public class MyThread4Spliterator<T> extends Thread 
10     // 寄存变量
11     private Spliterator<T> list;
12 
13     // 构造 - 传递参数
14     public MyThread4Spliterator(Spliterator<T> list) 
15         setList(list);
16     
17 
18     // 线程调用run
19     @Override
20     public void run() 
21         Spliterator<T> list2 = getList();
22         list2.forEachRemaining(new Consumer<T>() 
23 
24             @Override
25             public void accept(T t) 
26                 System.out.println(Thread.currentThread().getName()+" === "+t);
27             
28 
29         );
30     
31 
32     public Spliterator<T> getList() 
33         return list;
34     
35 
36     public void setList(Spliterator<T> list) 
37         this.list = list;
38     
39 

 

定义并拆分迭代器代码如下,需要注意拆分规则

 

 1 package com.FM.ArrayListStudy;
 2 
 3 import java.util.ArrayList;
 4 import java.util.Spliterator;
 5 
 6 public class SpliteratorInArrayListStudy 
 7     public static void main(String[] args) 
 8         // 初始化list
 9         ArrayList<Integer> list = new ArrayList<Integer>();
10         for (int i = 0; i < 20; i++) 
11             list.add(i + 1);
12         
13         //四线程均分配比方式
14         Spliterator<Integer> spliterator01 = list.spliterator();         //01中有20个元素
15         Spliterator<Integer> spliterator02 = spliterator01.trySplit();    //01中有10个元素,02中有10个元素
16         Spliterator<Integer> spliterator03 = spliterator01.trySplit();    //01中有5个元素,02中有10个元素,03中有5个元素
17         Spliterator<Integer> spliterator04 = spliterator02.trySplit();    //01中有5个元素,02中有5个元素,03中有5个元素,04中有5个元素
18         MyThread4Spliterator<Integer> t01 = new MyThread4Spliterator<Integer>(spliterator01);
19         MyThread4Spliterator<Integer> t02 = new MyThread4Spliterator<Integer>(spliterator02);
20         MyThread4Spliterator<Integer> t03 = new MyThread4Spliterator<Integer>(spliterator03);
21         MyThread4Spliterator<Integer> t04 = new MyThread4Spliterator<Integer>(spliterator04);
22         t01.setName("001");
23         t02.setName("002");
24         t03.setName("003");
25         t04.setName("004");
26         
27         t01.start();
28         t02.start();
29         t03.start();
30         t04.start();
31     
32 

运行结果:

 

Spliterator为jdk1.8新增接口,也是一个迭代器实现,从名称可以看出来,Spliterator是一个可分割的迭代器,用来分割和迭代给定源的元素,这里的源可以是collection,array和io等。 

该对象可以使用trySplit进行迭代器拆分,每次拆分后的迭代器接近上一次的二分之一。

这是官方对于大数据量数组多线程遍历加工的一种趋势性指引。

方法解析:

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>
   

private final ArrayList<E> 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

/** Create new spliterator covering the given  range */
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 : // divide range in half unless too small
        new ArrayListSpliterator<E>(list, lo, index = mid,
                                    expectedModCount);


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


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

 

以上是关于java 数据结构之arraylist Spliterator 使用和源码解析的主要内容,如果未能解决你的问题,请参考以下文章

String split 转 arrayList,java.lang.UnsupportedOperationException

JAVA集合之ArrayList源码分析

Java之ArrayList类(集合)

java集合之ArrayList

Java 之 ArrayList 类

Java源码之ArrayList分析