Java的Spliterator使用方式


Spliterator接口包含的方法:

boolean tryAdvance(Consumer action); 单个对下一个元素执行给定的动作,如果有剩下元素未处理,执行则返回true,否则返回false

default void forEachRemaining(Consumer action) 对剩余元素依次执行action函数

Spliterator trySplit(); 将一个Spliterator分割成多个Spliterator并返回。分割的Spliterator被用于每个子线程进行处理,从而达到并发处理的效果。

long estimateSize(); 该方法返回值并不会对代码正确性产生影响,但是会影响代码的执行线程数,后续会介绍一下

int characteristics(); 给出stream流具有的特性,不同的特性,不仅是会对流的计算有优化作用,更可能对计算结果会产生影响。

// 表明元素是有序的,如果是此特征值,那么trySplit分割之后的元素是有序的,tryAdvance处理元素也是有序的,forEachRemaining处理元素也是有序的。如果Collection#iterator是有序的,那么Collection也是有序的。List的遍历顺序是按照索引顺序,HashSet是无序的
public static final int ORDERED    = 0x00000010;
// 表明元素是去重的
public static final int DISTINCT   = 0x00000001;
// 表明遍历的顺序是排好序的,再此特征值下getComparator将会返回相关联的比较器或者null(自然排序),如果包含了此特征值,那么分割迭代器必然是ORDERED的
public static final int SORTED     = 0x00000004;
// 表明在遍历和分割之前 estimateSize 的返回值是一个有限的值,并且在没有源结构修改的情况下(增加、删除等),返回值将是遍历元素个数的准确值 这里需要看下 estimateSize 方法介绍
public static final int SIZED      = 0x00000040;
// 表明元素是非空的
public static final int NONNULL    = 0x00000100;
// 表示元素源不能进行结构修改的特征值;也就是说,无法添加,替换或删除元素,因此在遍历期间不会发生此类更改。不报告{IMMUTABLE}或{CONCURRENT}的Spliterator应该有关于遍历期间检测到的结构干扰的文档化策略(例如抛出{@link ConcurrentModificationException})。
public static final int IMMUTABLE  = 0x00000400;
//表示可以在没有外部同步的情况下由多个线程安全地同时修改元素源(允许添加,替换和/或删除)的特征值。如果是这样,Spliterator应该有一个关于遍历期间修改影响的书面政策。顶层Spliterator不应该同时返回{CONCURRENT}和{SIZED},因为如果已知,有限大小可能会在遍历期间同时修改源时发生更改。这样的Spliterator是不一致的,并且不能保证使用该Spliterator进行任何计算。如果子拆分大小已知,则子拆分器可以报告{@code SIZED},并且在遍历时不反映对源的添加或删除。大多数并发集合维护一致性策略,保证Spliterator构造点上存在的元素的准确性,但可能不反映后续的添加或删除。
public static final int CONCURRENT = 0x00001000;
// trySplit 返回的子分割迭代器都会是SIZED和SUBSIZED的。 分割成子,并且子的部分是固定大小的
public static final int SUBSIZED   = 0x00004000;

 

default Comparator getComparator() 对sorted的流,给出比较器。

 例子:

 

//具体实现可以去参考ArrayList的内部类ArrayListSpliterator
class ArrayListSpliterator<E> implements Spliterator<E>{ private final ArrayList<E> list; //当前迭代器所在位置 private int index; //容器总大小 private int fence; public ArrayListSpliterator(ArrayList<E> list, int index, int fence) { this.list = list; this.index = index; this.fence = fence; } //遍历list片段中的所有元素,并执行action @Override public void forEachRemaining(Consumer<? super E> action) { for(int i = index;i < fence;i++){ action.accept(list.get(i)); } } @Override public boolean tryAdvance(@NotNull Consumer<? super E> action) { if(index < fence){ action.accept(list.get(index)); return true; } return false; } //不同线程可以获取到不同的list片段,返回一个新的Spliterator对象 @Override public Spliterator<E> trySplit() { int current = index,end = fence,mid = ((current + end) % 2 == 0 ? (current + end) : (current + end) + 1) >>> 1; return (current >= mid) ? null : new ArrayListSpliterator<E>(list,current,index = mid); } //返回允许的最大线程数 @Override public long estimateSize() { return list.size() - index; } //返回特征值,但是我这里没有太搞懂 @Override public int characteristics() { return 0; } } //包含spliterator的线程 class MyThread extends Thread{ private static volatile Spliterator<String> spliterator; public MyThread() { super(); } public MyThread(ArrayListSpliterator spliterator) { super(); this.spliterator = spliterator; } @Override public void run() { Spliterator split = spliterator.trySplit(); if(split != null){ split.forEachRemaining(res->{ System.out.println(res); }); } } } class Main{ //因为当list的元素个数不同时,遍历需要的最少线程数也不同 public static void main(String[] args) throws IllegalAccessException { ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12,13,14)); ArrayListSpliterator arrayListSpliterator = new ArrayListSpliterator(list,0,list.size()); for(int i = 0;i < 4;i++){ Thread myThread = new MyThread(arrayListSpliterator); myThread.start(); } } }

  

引自:JAVA8 stream 中Spliterator的使用(一) | 并发编程网 – ifeve.com

引自:(8条消息) JAVA8学习10-spliterator 接口分析_z_yemu的博客-CSDN博客


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM