今天读了JDK1.8源码,知道了并行迭代器Spliterator

在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        谢谢~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/413563.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

牛客网挑战赛24 青蛙(BFS)

链接&#xff1a;https://www.nowcoder.com/acm/contest/157/E来源&#xff1a;牛客网 有一只可爱的老青蛙&#xff0c;在路的另一端发现了一个黑的东西&#xff0c;想过去一探究竟。于是便开始踏上了旅途 一直这个小路上有很多的隧道&#xff0c;从隧道的a进入&#xff0c;会从…

[css] 如何使用css显示a链接的url?

[css] 如何使用css显示a链接的url&#xff1f; .some-a-tag:before {content: attr(href); }个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

JAVA手写ArrayList以及LinkedList

手写记录一下~ 顶级接口List public interface List<E> {//返回线性表的大小public int getSize();//判断线性表中是否为空public boolean isEmpty();//判断线性表中是否包含元素oboolean contains(E o);//在线性表中查找元素o&#xff0c;若成功找到&#xff0c;返回其…

[css] css中的url()要不要加引号?说说你的理解

[css] css中的url()要不要加引号&#xff1f;说说你的理解 可以加&#xff0c;也可以不加。这个跟html标签的属性书写可以加引号也可以不加引号是一样的道理&#xff0c;当然如果属性中含有特殊字符比如空格则需要加空格&#xff0c;否则会引起浏览器解析错误。如果想养成良好…

JDK源码学习路线~每天学一点~每天进步一点点

很多java开发的小伙伴都会阅读jdk源码&#xff0c;然而确不知道应该从哪读起。以下为小编整理的通常所需阅读的源码范围。 标题为包名&#xff0c;后面序号为优先级1-4&#xff0c;优先级递减 1、java.lang 1) Object 1 2) String 1 3) AbstractStringBuilder 1 4) StringBuff…

[css] 使用css写一个垂直翻转图片的效果

[css] 使用css写一个垂直翻转图片的效果 transform: rotateX(180deg); /* 垂直镜像翻转 */个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

20.pipe

pipe相当于angular1里面的filter 做一些格式转换啊&#xff0c;或者从一个数组里面选取一个元素等等 只要你愿意可以定义很复杂的内容‘’ 我们先看看 angular2 里面自带的一些pipe 我们去我们的week3 下的problem-list下 我们到html里面 之前是这样的 之后是这样的 我们再写三…

TCP/UDP相关知识

三次握手&#xff1a; 为了方便描述我们将主动发起请求的172.16.50.72:65076 主机称为客户端&#xff0c;将返回数据的主机172.16.17.94:8080称为服务器。 第一次握手: 建立连接。客户端发送连接请求&#xff0c;发送SYN报文&#xff0c;将seq设置为0。然后&#xff0c;…

[css] 请写出font属性的快捷写法

[css] 请写出font属性的快捷写法 p { font:italic bold 12px/20px arial,sans-serif; }个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

Redis内部数据结构-跳跃表

今天学习了跳跃表&#xff0c;记录一下下~ 一、跳跃表简介 跳跃表是一种随机化数据结构&#xff0c;基于并联的链表&#xff0c;其效率可以比拟平衡二叉树&#xff0c;查找、删除、插入等操作都可以在对数期望时间内完成&#xff0c;对比平衡树&#xff0c;跳跃表的实现要简…

[css] 举例说明与打印有关的属性有哪些?

[css] 举例说明与打印有关的属性有哪些&#xff1f; page page-break-before page-break-after page-break-inside个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题…

MYSQL索引结构学习笔记

mysql 的数据、索引、DDL 等数据&#xff0c;都是以文件形式存储的&#xff0c; 所以导致每次查询都是一次I/O操作&#xff0c;当I/O操作过大时&#xff0c;会严重影响效率 MYSQL索引结构: mysql使用的是B树来存储索引的&#xff0c;为什么不用其他的呢&#xff1f;二叉树 -&g…

[css] 如何让背景图片固定不随滚动条滚动

[css] 如何让背景图片固定不随滚动条滚动 background-attachment:fixed个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

SKYLINE UVALive - 4108

我一开始没有想到用线段树&#xff0c;是我学得太僵了... 我们要记录每段的最大高度&#xff0c;而且要组织成区间信息&#xff0c;这要用到线段树 怎么用呢&#xff1f; 线段树维护区间最大值&#xff1b;对于每个线段&#xff0c;先二分至其包含区间&#xff0c;如果最大值&g…

Mybatis源码学习笔记

Mybatis核心概念: Configuration : 管理 mysql-config.xml 全局配置关系类 SqlSessionFactory: Session 管理工厂接口 Session: SqlSession 是一个面向用户&#xff08;程序员&#xff09;的接口。SqlSession 中提供了很多操作数据库的方法 Executor : 执行器是一个接口…

[css] 你用过css的tab-size属性吗?浏览器默认显示tab为几个空格?

[css] 你用过css的tab-size属性吗&#xff1f;浏览器默认显示tab为几个空格&#xff1f; tab-size 属性规定制表符&#xff08;tab&#xff09;字符的空格长度。在 HTML 中&#xff0c;制表符&#xff08;tab&#xff09;字符通常显示为一个单一的空格字符个人简介 我是歌谣&…

JQData数据提取及MySQL简单操作——基于Python

JQData平台真的挺不错&#xff0c;平台数据可以免费使用一年&#xff0c;满足绝大多数人需求&#xff0c;具体账号获取请自行百度哟~ 因需要高频数据而Wind也只给近三年&#xff0c;再要还得购&#xff0c;&#xff0c;机缘遇到这一平台&#xff0c;获得了账号试用很不错&#…

数据库事物相关笔记

一、数据库的事物的基本特性 事物是区分文件存储系统与Nosql数据库重要特性之一&#xff0c;其存在的意义是为了保证即使在并发情况下也能正确的执行crud操作。怎样才算是正确的呢&#xff1f;这时提出了事物需要保证的四个特性即ACID&#xff1a; A: 原子性(atomicity) 事物…

[css] clear属性只对块级元素有效么?为何无法应用于行内元素?

[css] clear属性只对块级元素有效么&#xff1f;为何无法应用于行内元素&#xff1f; block元素浮动之后已经脱离了文档流了&#xff0c;排列的顺序都不一样了&#xff0c;所以清除了之后有效果。 inline-block还是在文档流里面&#xff0c;加浮动不加浮动都没有什么作用的&am…

GO语言-基础语法:条件判断

1. IF判断(aa.txt内容&#xff1a;asdfgh。bb.txt内容&#xff1a;12345) package mainimport ("io/ioutil""fmt" )func main() {const filename1, filename2 "aa.txt", "bb.txt"contents, err : ioutil.ReadFile(filename1) if err …