在JDK1.8的ArrayList里面偶然看到了这个内部类,同时对比了1.7的版本,发现1.7并没有这后面的东西, 随着好奇心,就搜了一下下,发现很有意思~ 也查了一些资料,如下总结:
Spliterator是什么?
Spliterator是一个可分割迭代器(splitable iterator),可以和iterator顺序遍历迭代器一起看。jdk1.8发布后,对于并行处理的能力大大增强,Spliterator就是为了并行遍历元素而设计的一个迭代器,jdk1.8中的集合框架中的数据结构都默认实现了spliterator,后面我们也会结合ArrayList中的spliterator()一起解析。
Spliterator内部结构
//单个对元素执行给定的动作,如果有剩下元素未处理返回true,否则返回false
boolean tryAdvance(Consumer<? super T> action);//对每个剩余元素执行给定的动作,依次处理,直到所有元素已被处理或被异常终止。默认方法调用tryAdvance方法
default void forEachRemaining(Consumer<? super T> action) {do { } while (tryAdvance(action));
}//对任务分割,返回一个新的Spliterator迭代器
Spliterator<T> trySplit();//用于估算还剩下多少个元素需要遍历
long estimateSize();//当迭代器拥有SIZED特征时,返回剩余元素个数;否则返回-1
default long getExactSizeIfKnown() {return (characteristics() & SIZED) == 0 ? -1L : estimateSize();
}//返回当前对象有哪些特征值
int characteristics();//是否具有当前特征值
default boolean hasCharacteristics(int characteristics) {return (characteristics() & characteristics) == characteristics;
}
//如果Spliterator的list是通过Comparator排序的,则返回Comparator
//如果Spliterator的list是自然排序的 ,则返回null
//其他情况下抛错
default Comparator<? super T> getComparator() {throw new IllegalStateException();
}
JDK8源码内的ArrayList中的ArrayListSpliterator
static final class ArrayListSpliterator<E> implements Spliterator<E> {//用于存放ArrayList对象private final ArrayList<E> list;//起始位置(包含),advance/split操作时会修改private int index;//结束位置(不包含),-1 表示到最后一个元素private int fence;//用于存放list的modCountprivate int expectedModCount;ArrayListSpliterator(ArrayList<E> list, int origin, int fence,int expectedModCount) {this.list = list;this.index = origin;this.fence = fence;this.expectedModCount = expectedModCount;}//获取结束位置(存在意义:首次初始化石需对fence和expectedModCount进行赋值)private int getFence() {int hi;ArrayList<E> lst;//fence<0时(第一次初始化时,fence才会小于0):if ((hi = fence) < 0) {//list 为 null时,fence=0if ((lst = list) == null)hi = fence = 0;else {//否则,fence = list的长度。expectedModCount = lst.modCount;hi = fence = lst.size;}}return hi;}//分割list,返回一个新分割出的spliterator实例public ArrayListSpliterator<E> trySplit() {//hi为当前的结束位置//lo 为起始位置//计算中间的位置int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;//当lo>=mid,表示不能在分割,返回null//当lo<mid时,可分割,切割(lo,mid)出去,同时更新index=midreturn (lo >= mid) ? null :new ArrayListSpliterator<E>(list, lo, index = mid, expectedModCount);}//返回true 时,只表示可能还有元素未处理//返回false 时,没有剩余元素处理了。。。public boolean tryAdvance(Consumer<? super E> action) {if (action == null)throw new NullPointerException();//hi为当前的结束位置//i 为起始位置int hi = getFence(), i = index;//还有剩余元素未处理时if (i < hi) {//处理i位置,index+1index = 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 loopArrayList<E> lst; Object[] a;if (action == null)throw new NullPointerException();if ((lst = list) != null && (a = lst.elementData) != null) {//当fence<0时,表示fence和expectedModCount未初始化,可以思考一下这里能否直接调用getFence(),嘿嘿?if ((hi = fence) < 0) {mc = lst.modCount;hi = lst.size;}elsemc = expectedModCount;if ((i = index) >= 0 && (index = hi) <= a.length) {for (; i < hi; ++i) {@SuppressWarnings("unchecked") E e = (E) a[i];//调用action.accept处理元素action.accept(e);}//遍历时发生结构变更时抛出异常if (lst.modCount == mc)return;}}throw new ConcurrentModificationException();}public long estimateSize() {return (long) (getFence() - index);}public int characteristics() {//打上特征值:、可以返回sizereturn Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;}
}
以上为源码讲解部分,大意是说,这个就是用来多线程并行迭代的迭代器,这个迭代器的主要作用就是把集合分成了好几段,每个线程执行一段,因此是线程安全的。基于这个原理,以及modCount的快速失败机制,如果迭代过程中集合元素被修改,会抛出异常。
我们设计一个测试用例:创建一个长度为100的list,如果下标能被10整除,则该位置数值跟下标相同,否则值为aaaa。然后多线程遍历list,取出list中的数值(字符串aaaa不要)进行累加求和。
测试代码如下:
package com.turingschool.demo.ds;import java.util.ArrayList;
import java.util.List;
import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.regex.Pattern;import org.junit.Test;public class Atest {AtomicInteger count = new AtomicInteger(0);List<String> strList = createList();Spliterator spliterator = strList.spliterator();/*** 多线程计算list中数值的和 测试spliterator遍历*/@Testpublic void mytest() {for (int i = 0; i < 4; i++) {new MyThread().start();}try {Thread.sleep(15000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("结果为:" + count);}class MyThread extends Thread {@SuppressWarnings("unchecked")@Overridepublic void run() {final String threadName = Thread.currentThread().getName();System.out.println("线程" + threadName + "开始运行-----");spliterator.trySplit().forEachRemaining(new Consumer() {@Overridepublic void accept(Object o) {if (isInteger((String) o)) {int num = Integer.parseInt(o + "");count.addAndGet(num);System.out.println("数值:" + num + "------" + threadName);try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}});System.out.println("线程" + threadName + "运行结束-----");}}private List<String> createList() {List<String> result = new ArrayList<>();for (int i = 0; i < 100; i++) {if (i % 10 == 0) {result.add(i + "");} else {result.add("aaa");}}return result;}public static boolean isInteger(String str) {Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");return pattern.matcher(str).matches();}
}
运行结果为:
从结果可以看出,四个线程遍历没有产生并发问题,
本文参考:https://www.cnblogs.com/nevermorewang/p/9368431.html 谢谢~