转换 Iterator 为 Java 8 的 Stream

Java 中有关抽象的可遍历的对象有 Iterator, Iterable 和 Java 8 的 Stream, Iterable 可简单的用如下代码转换为 Stream

StreamSupport.stream(iterable.spliterator(), false)

再回过头来,为什么要把 Iterator 或 Iterable 转换为 Stream, 因为 Iterator 和 Iterable 只提供有限的遍历操作,如 Iterator 接口的全部四个方法

hasNext()
next()
forEachRemaining(consumer)
remove()

同样 Iterable 也只有 iterator(), forEach(consumer), 和 spliterator() 方法。而 Java 8 的 Stream 就大不一样的,带有大量的链式操作方法,如 filter, map, flatMap, collect 等。

因此如果我们已有一个 Iterator 类型,能够被转换为 Stream 类型的话将会大大简化后续的转换,处理操作。具体的从 Iterator 到 Stream 的转换方式有两种

通过 Spliterators.spliteratorUnknownSize(...) 方法变 Iterator 为 Stream

由于 Iterator 的大小是不确定的,有多少个元素完全由 hasNext() 决定的,spliteratorUnknownSize() 方法正好应了这一情景。代码如下

输出会是

[3, 1, 2, null, 2]

前面的 Spliterator.SORTED 参数值是 characteristics, 预定义了七个常量值,但是对于 Spliterators.splieratorUnknownSize(...) 方法来说无论传什么都不会影响到最终的结果。比如我们可以做下面一个测试

输出结果如下:

characteristics 4096: [3, 1, 2, null, 2]
characteristics 1: [3, 1, 2, null, 2]
characteristics 1024: [3, 1, 2, null, 2]
characteristics 256: [3, 1, 2, null, 2]
characteristics 64: [3, 1, 2, null, 2]
characteristics 4: [3, 1, 2, null, 2]
characteristics 16384: [3, 1, 2, null, 2]

这里的 characteristics 传什么都行。

根据下面的分析,characteristics 用不着从常量定义中挑选,直接给 0 就行,写成下面那样

Spliterators.spliteratorUnknownSize(sourceIterator, 0)

经由 Iterable 把 Iterator 转换为 Stream

像最前面那样 Iterable 可以轻松转换为 Stream, 所以先把 Iterator 变为 Iterable 再转化为 Stream。

注意到上面由一个 Lambda 变 Iterator 为 Iterable 了,看 Iterable 接口的源代码

只有一个抽象方法(其他两个为默认方法),所以可用

Iterable<Integer> iterable = () -> sourceIterator

声明一个 iterator() 返回 sourceIterator 的 Iterable 类型。

再看 Iterable 的默认方法 spliterator() 的实现,同样是调用的

Spliterators.spliteratorUnknownsSize(iterator(), 0)

这里的第二个参数 0 实际上不是 Spliterator 中的 CONCURRENT, DISTINCT, IMMUTABLE, NONNULL, SIZED, SORTED, SUBSIZED 中的任何一个值。

写到这里,通过参源代码阅读,前面所述的两种方式实质上没有一点区别。

对由 Iterator 转换为 Stream 的一个测试

下面例子创建一个无限大小的 Iterator (hasNext() 永远返回 true),然后由它转换成 Stream, 再调用 Stream 的 filter 和 limit 来检验它是一个真正的 Stream

下面是某一次的执行输出

next: 1
next: 2
next: 3
[11430, 20177, 64297]

next: 4
next: 5
next: 6
next: 7
next: 8
next: 9
next: 10
next: 11
next: 12
next: 13
next: 14
next: 15
next: 16
next: 17
[19378, 16142, 9354]

该行为与 Stream 是相吻合的,因为 Stream 是一个 Lazy 的,它确实是一个流,无需事选知道流中将会有多少元素。

本文链接 https://yanbin.blog/convert-iterator-to-java-8-stream/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

2 Comments
Inline Feedbacks
View all comments
Zhou
Zhou
4 years ago

发现你又更新了