Collection如何转成stream

在java 1.8中,Collection新增了一个default方法stream(),他可以将集合转换成流,那么这节我将会深入源码看看具体过程是如何。

Collection中的操作

首先查看Collection中的stream方法

default Stream<E> stream() {return StreamSupport.stream(spliterator(), false);
}
复制代码

该方法调用了StreamSupport.stream方法,在前面我分析了StreamSupport的这个方法的执行步骤,该方法接收一个Spliterator参数以及是否是并发的判断,主要的数据保存在Spliterator中。此时我们再看看spliterator()方法

@Override
default Spliterator<E> spliterator() {return Spliterators.spliterator(this, 0);
}
复制代码

这是一个重写方法,它的父类方法如下:

default Spliterator<T> spliterator() {return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
复制代码

该方法调用Spliterators.spliteratorUnknownSize()方法,并传入一个迭代器和特征值,而Collection中重写的方法调用的是Spliterators.spliterator(),传递的是一个Collection对象和特征值.
我们直接看Spliterator.spliterator()方法

public static <T> Spliterator<T> spliterator(Collection<? extends T> c,int characteristics) {return new IteratorSpliterator<>(Objects.requireNonNull(c),characteristics);
}
复制代码

它先判断传入的集合是否为空,然后返回一个IteratorSpliterator对象,接着我们看看这个对象的构造方法

public IteratorSpliterator(Collection<? extends T> collection, int characteristics) {this.collection = collection;this.it = null;this.characteristics = (characteristics & Spliterator.CONCURRENT) == 0? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED: characteristics;
}
复制代码

该构造方法的作用是根据给定的集合创建一个spliterator。该方法位于Spliterators类下面,根据java8对于这些类-接口的设计模式,该类主要用于对Spliterator的具体静态实现。因此我们直接看IteratorSpliterator类的继承关系

文档上对于它的描述为:

使用给定迭代器进行元素操作的Spliterator。 同时实现分裂器trySplit()以允许有限的并行性。

可见,该类主要用于迭代器生成Spliterator,并且允许一定的并发性。 既然生成了Spliterator对象,那么就可以直接调用StreamSupport.stream(spliterator: Spliterator, parallel : boolean)方法创建Stream了。

Spliterator对其操作的实现

这节主要介绍上一节的迭代器Spliterator的具体实现,让我们看看jdk作者们是如何对有序的集合进行并发操作的。

首先我们看看IteratorSpliterator新定义的参数

static final int BATCH_UNIT = 1 << 10;  // batch array size increment
static final int MAX_BATCH = 1 << 25;  // max batch array size;
private final Collection<? extends T> collection; // null OK
private Iterator<? extends T> it;
private final int characteristics;
private long est;             // size estimate
private int batch;            // batch size for splits
复制代码

其中我们需要关注的是batch参数,因为他就是涉及到如何拆分,该参数的作用是确定拆分的大小。而这个characteristics便是它的特征值,这个值直接关系到生成stream时候限定的各个阶段能进行的操作。 对特征值的计算如下:

characteristics = (characteristics & Spliterator.CONCURRENT) == 0? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED: characteristics;
复制代码

其中的Spliterator.CONCURRENT参数表示可以在没有外部同步的情况下由多个线程安全地同时修改元素源(允许添加,替换和/或删除)的特征值。这里的判断为:如果给定的特征值不允许并行,那么该特征值允许按顺序遍历(Spliterator.SIZED)并且该数据由Spliterator产生(Spliterator.SUBSIZED),否则直接保持原来的特征值。

关于特征值的计算我之前一直没懂为什么都是用 | 和 & 这两个运算符号,直到现在我才发现,这是为了在有限的位数中尽可能地多保存信息。而|是为了增加信息,&是为了判断是否存在该信息。比如:

有一个List,对于它的操作由一组特征值限定。

  • 0b001 - 允许增加
  • 0b010 - 允许删除
  • 0b100 - 允许修改

当这个List的特征值为0b011时:
    0b001 & 0b011的结果为1,0b011 & 0b010的结果为1,0b011 & 0b100的结果为0,则该List允许增加、删除,但是不允许修改。
那么如何让他允许修改呢?
    只要将特征值修改为0b011 | 0b100即可,该结果为7转换成二进制则为0b111.

接着查看重头戏,IteratorSpliterator对trySplit的实现:

@Override
public Spliterator<T> trySplit() {Iterator<? extends T> i;long s;if ((i = it) == null) {i = it = collection.iterator();s = est = (long) collection.size();}elses = est;if (s > 1 && i.hasNext()) {int n = batch + BATCH_UNIT;if (n > s)n = (int) s;if (n > MAX_BATCH)n = MAX_BATCH;Object[] a = new Object[n];int j = 0;do { a[j] = i.next(); } while (++j < n && i.hasNext());batch = j;if (est != Long.MAX_VALUE)est -= j;return new ArraySpliterator<>(a, 0, j, characteristics);}return null;
}
复制代码

这里的操作我们对应上面的构造方法,我们的流程是使用集合来构造Spliterator的因此,表示迭代器的it为null。因此会执行第一个条件判断

if ((i = it) == null) {i = it = collection.iterator();s = est = (long) collection.size();
}
复制代码

此时会将集合中的迭代器赋值给i和it,集合中的元素个数赋值给s和est。否则就将迭代器中的元素个数赋值给s。

当元素的个数大于1的时候int n的值为拆分的批量大小与批量大小数组增量(1 << 10 = 2 ^ 10 = 1024)的和。如果该值大于元素的个数,则将n赋值为元素个数,如果该值大于最大批量数组的大小(1 << 25 = 2 ^ 25 = 0x2000000)则将n赋值为最大批量数组的大小。然后创建Object[]数组,数组大小为n,将迭代器中的元素添加进数组,然后将拆分的批量大小(batch)赋值为循环赋值次数,如果最开始迭代器中的元素个数不等于Long的最大值(0x7fffffffffffffffL),则est减去循环次数,即est为剩下的元素的个数。将赋值后的Object[]作为参数创建ArraySpliterator对象。

官方给这段代码的注释为:

分成算术增加批量大小的数组。 如果每个元素的Consumer操作比将它们转移到数组中更昂贵,那么这只会提高并行性能。 在分割大小中使用算术级数提供了开销与并行性边界,这些边界不会特别有利于或惩罚轻量级与重量级元素操作的情况,跨越#elements与#cores的组合,无论是否已知。 我们生成O(sqrt(#elements))分割,允许O(sqrt(#cores))潜在的加速。

而对于元素个数的边界为批量大小数组的增量BATCH_UNIT(1 << 10 = 1024).即将1024个元素分成一组进行并发操作.

Collection如何转成stream以及Spliterator对其操作的实现相关推荐

  1. Java 8 Stream 的终极技巧——Collectors 操作

    1. 前言 昨天在 Collection 移除元素操作[1] 相关的文章中提到了 Collectors .相信很多同学对这个比较感兴趣,那我们今天就来研究一下 Collectors . 2. Coll ...

  2. lambda表达式——Stream管道流的map操作

    lambda表达式--Stream管道流的map操作 一.回顾Stream管道流map的基础用法 二.处理非字符串类型集合元素 三.再复杂一点:处理对象数据格式转换 四.flatMap 一.回顾Str ...

  3. java 中遍历双列集合_获取单列集合,双列集合,数组的Stream流对象以及简单操作...

    获取流对象 获取单列集合,双列集合,数组的流对象 单列集合获取流对象: 1.java.util.Collection接口中加入了default方法stream()获取流对象,因此其所有实现类均可通过此 ...

  4. 进行将多张CAD图纸转换成高清WMF格式的操作是什么?

    进行将多张CAD图纸转换成高清WMF格式的操作是什么?WMF格式是图片格式中的一种,为了方便将CAD图纸文件进行查看我们会需要将其进行格式间的转换操作,将其进行转换成图片格式中的WMF格式就是其中的一 ...

  5. java stream 分组求和_Java stream List 求和、分组操作

    Java stream List 求和.分组操作 前言 项目中经常会使用Stream操作一些集合数据,今天记录一下我经常使用的Stream操作 求和操作public static void main( ...

  6. caj转换成word转换器下载后如何操作?

    我们办公会用到caj文件,但是caj文件不好编辑和转换的,所以我们可以用caj转换成word转换器,caj转换成word转换器下载后如何操作?我这就告诉你2种方法. 方法1: 步骤一:在百度中去搜索软 ...

  7. HTML collection数组转换成正常的dom对象数组

    HTML collection对象是一个类数组,如何将它转化为正常的数组,我们可以这样: var obj = document.getElementsByClassName('songlists') ...

  8. Stream流:基本API操作详细笔记

    目录 1.Stream流基础介绍 1.1 Stream流有一些特性: 1.2 流的操作可以分为两种类型: 2.创建流 2.1 串行流: 2.2 并行流 3.操作流 3.1 过滤 3.2 映射 3.3 ...

  9. java peek函数_Java 8 Stream Api 中的 peek 操作

    1. 前言 我在 Java 8 Stream API中的 map 和flatMap 中讲述了Java8 Stream API中 map 操作和 flatMap 操作的区别.然后有小伙伴告诉我 peek ...

最新文章

  1. Oracle 升级10.2.0.5.4 OPatch 报错Patch 12419392 Optional component(s) missing 解决方法
  2. java和python根据对象某一个属性排序
  3. XOR Specia-LIS-t 异或和 贪心
  4. 2017.6.27 树上操作 思考记录
  5. 快来,前方美女出没!!
  6. Linux 给普通用户分配root权限或给用户分配多个用户组
  7. vue base64加密对象_想加密JavaScript怎么办,试试这款加密库!
  8. python多级目录import_Python之路---包和模块
  9. 航空机场三字码和航空公司二字码
  10. Chromium网页Layer Tree绘制过程分析
  11. obs,直播文字画面模糊处理
  12. 一种面向业务流的内存管理算法
  13. 计算机学的痛苦可以换专业,在大学里选错了专业,是一种怎样的“痛苦”体验?...
  14. word中如何去掉文档右侧带格式的批注框
  15. 微信小程序实现文字识别-ocr插件
  16. Snipaste 免费截图贴图工具(良心推荐)
  17. apple id是什么意思
  18. 网站设计风格有很多种,看看哪种适合你?
  19. “Why Should I Trust you ?”Explaining the Predictions of Any Classififier.-对分类预测进行解释
  20. CSS3 动画效果

热门文章

  1. python基础网易_看看你的Python基础怎么样?
  2. Python协程原理介绍及基本使用
  3. 剑指offer面试题[35]-第一个只出现一次的字符
  4. Android中如何实现多个框,在android中的对话框中设置多个文本框
  5. 手工建立mysql的服务_怎样自已手工打开mysql服务
  6. UAT测试的带队经验
  7. axure9数据统计插件_数据分析太棘手?常用8大统计软件解决难题!
  8. 360网神防火系统 配置端口映射_IIS配置证书端口443无法访问
  9. python代码注释规范-Python编程规范之注释
  10. oracle数据库数据消失,,保存在数据库里的数据莫名其妙的消失