本文转载自 jdk8 Stream 解析2 - Spliterator分割迭代器

概述

我们最为常见的流的产生方式是 collection.stream(), 你点开Stream()方法, 他是通过 Collection 这个上层接口两个java8 新增特性 default method 进行实现。

    @Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
} default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}

这就牵扯到一个关键要点 Spliterator :分割迭代器。

本文主要描述 Spliterator 的作用,大部分来源于你不愿意读的 JavaDoc

Spliterator 是 jdk8 非常重要的概念。里面的细节很值得学习玩味。

Spliterator 冗长的JavaDoc 说了什么?

基本介绍

Spliterator 是一个对源(数组,集合,io流等等)中元素进行遍历和分区的类。

可以通过 tryAdvance() 方法逐个遍历,也可以按照 forEachRemaining() 方法进行按 bulk 逐块的遍历。(内部调用的还是tryAdvance

Spliterator 有类似 Collector 中的 characteristics , 但都是由十六进制来表示的。

  • SIZED :表示大小固定, Collection常用
  • DISTINCT : 去重, Set常用
  • SORTED : 有顺序的 SortedSet 会用
  • 等等

原生类型的特化版本

原始类型特化的分割迭代器也被提供,和Stream类似。减少装箱和拆箱的操作

比迭代器Iterator 更加高效的遍历元素的方式

提供更加高效的方法,进行数据的迭代。
Iterator 的使用需要调用两个组合方法 hasNext() 以及 next() ,同时在多线程访问的情况下还会出现竞争,你需要去同步。
而分割迭代器 Spliterator 使用函数式编程的方式,只用一个方法就可以做到这个两个函数动作。就避免了竞争 ,就是 tryAdvance() 方法。后面会介绍

Spliterator的接口方法 

tryAdvance()

同时做了 hasNext() 以及 next() 的工作。

/**
* 类似于普通的 Iterator ,它会按顺序一个一个使用 Spliterator 中的元素执行action,并且如果还有其他元素要遍历就返回 true,否则返回 false。
*/
boolean tryAdvance(Consumer<? super T> action);

forEachRemaining()

是一个默认方法,对余下的元素进行操作,直到元素全部被遍历完
一般情况下会直接调用上面的tryAdvance() 方法,但是也可以根据需要进行重写。

/**
* 对余下的元素进行操作,直到元素全部被遍历完
* 如果源是有序的,遍历也是有序的
*/
default void forEachRemaining(Consumer<? super T> action) {
do { } while (tryAdvance(action));
}

这里有一点很值得注意,方法体中的 do {} 是空的,这个是因为 tryAdvance() 方法本身就完成了两个操作 hasNext() 以及 next(),所以方法体中不需要有任何操作了。这个是 函数式编程带来的好处。以及与命令式编程的区别。

trySplit()

尝试切分源来的 Spliterator, 返回的是(注意!!!)返回的是 分割出来的那一部分 数据,原有的数据集将不再包含这部分数据集合。两者 没有交集。剩下的可以继续分割,也许不可以继续分割了。

举个例子,我原来有 100个元素,我通过 trySplit 切分出 30 个,作为一个新的 Spliterator分割迭代器 返回,原有的,就还剩下 70 个。

  • 如果是原有数据集合是 ORDERD 的,分出来的也是有序的。
  • 除非元素数量是无穷的,否则,最后一定会出现不能再分割的情况,这种情况下,返回的结果是 null。
Spliterator<T> trySplit();

estimateSize()

估算还剩下多少个元素需要遍历,不一定精确。
但是如果这个 Spliterator 是 SIZED,没有被遍历或者 split, 或是 SUBSIZED的,没有被遍历,那么他这个值一定是准确的。

long estimateSize();

还有个与之相关的默认方法,就是利用这个特性。

default long getExactSizeIfKnown() {
return (characteristics() & SIZED) == 0 ? -1L : estimateSize();
}

characteristics()

  • 分割之前,返回的结果都是一致的
  • 而分割之后,不保证一致

有一个默认方法用于判断 Spliterator 是否包含这个特性

default boolean hasCharacteristics(int characteristics) {
return (characteristics() & characteristics) == characteristics;
}

getComparator

如果源是SORTED 类型的,且有比较器 Comparator 的话,则返回这个 Comparator,如果是SORTED 类型的,但是没有比较器,则返回 null , 除此之外,都抛出异常。

接口的默认方法里,就是抛出了异常。

default Comparator<? super T> getComparator() {
throw new IllegalStateException();
}

Spliterator的8个Characteristics 特性

ORDERED

源的元素有序,tryAdvance ,forEachRemaining和 trySplit 都会保证有序的进行元素的处理。

  • 需要注意 hashSet 这类 Collection 是不保证有序的
  • ORDERED 特性的数据,在并发计算的时候客户端也要做顺序限制的保证

DISTINCT

太简单,唯一性。 类似 Set 这样的传入集合会拥有这样的特性

SORTED

有这种特性的 Spliterator ,有一个特定的顺序。或者是所有元素都是可比较的,或者是有特定的比较器。

有 SORTED 一定会有 ORDERED

SIZED

有这种属性的 Spliterator 在遍历和分割之前,estimateSize() 返回的大小是固定的,并且是准确的。

NONNULL

不为 NULL, 大部分并发的集合,队列,Map 都可能会有这样的特性。

IMMUTABLE

不可变的。元素遍历期间不可以被 添加,替换,删除(cannot be added, replaced, or removed),否则,应该抛出异常。

CONCURRENT

支持并发操作的。

  • 顶层的 Spliterator 不可以 CONCURRENT 与 SIZED。 这两者是相互冲突的。
  • 但是分割之后的 Spliterator , 可能是 SIZED, 顶层不能决定底层

SUBSIZED

该 Spliterator 和所有从它拆分出来的分割迭代器都是 SIZED 以及 SUBSIZED 的。
如果分割后,没有按照要求返回SIZED 以及 SUBSIZED 属性,那么操作是不被保证的,也就是结果不可预测。

这个属性和 SIZED 的区别就是, SIZED 不保证 SUBSIZED。而 SUBSIZED 会要求保证 SIZED

内部特化而做的函数式接口 (OfPrimitive)

除了上面的函数,以及特性,Spliterator 迭代器中,还有几个定义在内部的接口。

OfPrimitive 重载了(overloads)了 Spliterator 的方法。用于实现特化的分割迭代器。

overloads:参数列表不同,函数名相同,与返回值类型无关,与访问修饰符无关。

注意与 override 的区别

免责声明:
本文转自网络文章,转载此文章仅为个人收藏,分享知识,如有侵权,请联系博主进行删除。
原文作者:待葡萄嗖透
原文出处:http://movingon.cn/2017/05/02/jdk8-Stream-%E8%A7%A3%E6%9E%902-Spliterator%E5%88%86%E5%89%B2%E8%BF%AD%E4%BB%A3%E5%99%A8/

Java8系列 (三) Spliterator可分迭代器的更多相关文章

  1. Java8系列 (七) CompletableFuture异步编程

    概述 Java8之前用 Future 处理异步请求, 当你需要获取任务结果时, 通常的做法是调用  get(long timeout, TimeUnit unit) 此方法会阻塞当前的线程, 如果任务 ...

  2. 前端构建大法 Gulp 系列 (三):gulp的4个API 让你成为gulp专家

    系列目录 前端构建大法 Gulp 系列 (一):为什么需要前端构建 前端构建大法 Gulp 系列 (二):为什么选择gulp 前端构建大法 Gulp 系列 (三):gulp的4个API 让你成为gul ...

  3. Web 开发人员和设计师必读文章推荐【系列三十】

    <Web 前端开发精华文章推荐>2014年第9期(总第30期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML5 ...

  4. MyBatis学习系列三——结合Spring

    目录 MyBatis学习系列一之环境搭建 MyBatis学习系列二——增删改查 MyBatis学习系列三——结合Spring MyBatis在项目中应用一般都要结合Spring,这一章主要把MyBat ...

  5. MySQL并发复制系列三:MySQL和MariaDB实现对比

    http://blog.itpub.net/28218939/viewspace-1975856/ 并发复制(Parallel Replication) 系列三:MySQL 5.7 和MariaDB ...

  6. WCF编程系列(三)地址与绑定

    WCF编程系列(三)地址与绑定   地址     地址指定了接收消息的位置,WCF中地址以统一资源标识符(URI)的形式指定.URI由通讯协议和位置路径两部分组成,如示例一中的: http://loc ...

  7. 【JAVA编码专题】 JAVA字符编码系列三:Java应用中的编码问题

    这两天抽时间又总结/整理了一下各种编码的实际编码方式,和在Java应用中的使用情况,在这里记录下来以便日后参考. 为了构成一个完整的对文字编码的认识和深入把握,以便处理在Java开发过程中遇到的各种问 ...

  8. SQL Server 2008空间数据应用系列三:SQL Server 2008空间数据类型

    原文:SQL Server 2008空间数据应用系列三:SQL Server 2008空间数据类型 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft SQL Server ...

  9. VSTO之旅系列(三):自定义Excel UI

    原文:VSTO之旅系列(三):自定义Excel UI 本专题概要 引言 自定义任务窗体(Task Pane) 自定义选项卡,即Ribbon 自定义上下文菜单 小结 引言 在上一个专题中为大家介绍如何创 ...

随机推荐

  1. 56、Spark Streaming: transform以及实时黑名单过滤案例实战

    一.transform以及实时黑名单过滤案例实战 1.概述 transform操作,应用在DStream上时,可以用于执行任意的RDD到RDD的转换操作.它可以用于实现,DStream API中所没有 ...

  2. react用脚手架创建一个react单页面项目,react起手式

    官网地址:https://react.docschina.org/ 确保本地安装了Node.js node的版本大于8.10    npm的版本大于5.6 1.在本地的某个位置创建一个文件夹,执行以下 ...

  3. 【大数据作业十一】分布式并行计算MapReduce

    作业要求:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/3319 1.用自己的话阐明Hadoop平台上HDFS和MapReduce的功 ...

  4. docker容器启动后添加端口映射

    DOCKER 给运行中的容器添加映射端口 方法1 1.获得容器IP 将container_name 换成实际环境中的容器名 docker inspect `container_name` | grep ...

  5. 一次解决idea maven settings.xml文件不生效

    一直使用eclipse,在eclipse中可以手动指定global和user settings,并且settings的文件名称可以不是settings.xml,只要内容符合maven规范就行.如下: ...

  6. 创建批处理文件.bat文件(删除指定文件夹下的文件及文件夹并循环)

    1.针对仅仅是删除文件夹下的文件的操作:使用del命令,单纯的删除文件操作,如下:del /f /s /q C:\Users\dell\AppData\Local\Temp\*.* 2.删除文件夹操作 ...

  7. redis 服务器开放给其他电脑连接

    1.云服务器的端口6379开通 2.宝塔服务器上的6379开通 3.修改服务器上的redis配置文件: # bind 127.0.0.1 注释掉daemonize no 改为noprotected-m ...

  8. (8)Flask微电影项目会员中心其他页面搭建

    会员中心修改密码.评论.登录日志和收藏电影4个页面的内容. 一.修改密码页面: {% extends "home/home.html" %} {% block css %} < ...

  9. openresty开发系列30--openresty中使用全局缓存

    openresty开发系列30--openresty中使用全局缓存 Nginx全局内存---本地缓存 使用过如Java的朋友可能知道如Ehcache等这种进程内本地缓存.Nginx是一个Master进 ...

  10. ISO/IEC 9899:2011 摘要

    本国际标准指定了C编程语言的形式并建立了对用它所表达的程序的解释.其目的在于促进在多种计算机系统上的C语言程序的可移植性.可靠性.可维护性以及高效的执行. 为了详细地说明C语言本身以及C语言执行库,包 ...