转换 Iterator 为 Java 8 的 Stream

栏目: 编程语言 · Java · 发布时间: 4年前

内容简介:Java 中有关抽象的可遍历的对象有 Iterator, Iterable 和 Java 8 的 Stream, Iterable 可简单的用如下代码转换为 Stream再回过头来,为什么要把 Iterator 或 Iterable 转换为 Stream, 因为 Iterator 和 Iterable 只提供有限的遍历操作,如 Iterator 接口的全部四个方法同样 Iterable 也只有

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() 方法正好应了这一情景。代码如下

Iterator<Integer> sourceIterator = Arrays.asList(3, 1, 2, null, 2).iterator();
Stream<Integer> targetStream = StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(sourceIterator, Spliterator.SORTED), false);
 
System.out.println(Arrays.toString(targetStream.toArray()));

输出会是

[3, 1, 2, null, 2]

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

    public void test(int characteristics) {
        System.out.printf("characteristics %5d: ", characteristics);
        Iterator<Integer> sourceIterator = Arrays.asList(3, 1, 2, null, 2).iterator();
        Stream<Integer> targetStream = StreamSupport.stream(
            Spliterators.spliteratorUnknownSize(sourceIterator, characteristics), false);
        System.out.println(Arrays.toString(targetStream.toArray()));
    }
 
    Arrays.asList(
        Spliterator.CONCURRENT,
        Spliterator.DISTINCT,
        Spliterator.IMMUTABLE,
        Spliterator.NONNULL,
        Spliterator.SIZED,
        Spliterator.SORTED,
        Spliterator.SUBSIZED).forEach(this::test);

输出结果如下:

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。

Iterator<Integer> sourceIterator = Arrays.asList(3, 1, 2, null, 2).iterator();
Iterable<Integer> iterable = () -> sourceIterator;
Stream<Integer> targetStream = StreamSupport.stream(iterable.spliterator(), false);
 
System.out.println(Arrays.toString(targetStream.toArray()));

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

public interface Iterable<T> {
    Iterator<T> iterator();
 
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
 
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

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

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

    public Stream<Integer> convert(Iterator<Integer> sourceIterator) {
        Iterable<Integer> iterable = () -> sourceIterator;
        return StreamSupport.stream(iterable.spliterator(), false);
    }
    
    @Test
    public void test() {
        Iterator<Integer> sourceIterator = new Iterator<Integer>() {
            private AtomicInteger count = new AtomicInteger(0);
            private Random random = new Random();
 
            @Override
            public boolean hasNext() {
                return true;
            }
 
            @Override
            public Integer next() {
                System.out.println("next: " + count.incrementAndGet()); //每一次遍历将会打印计数
                return random.nextInt(99999);
            }
        };
 
        //无条件的获得 3 个元素即可
        System.out.println(Arrays.toString(convert(sourceIterator).limit(3).toArray()));
        System.out.println();
 
        //从流中过虑出小于 30000 的 3 个元素
        System.out.println(Arrays.toString(convert(sourceIterator).filter(a -> a < 30000).limit(3).toArray()));
    }

下面是某一次的执行输出

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 的,它确实是一个流,无需事选知道流中将会有多少元素。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

网站开发案例课堂:HTML5+CSS3+JavaScript网页设计案例课堂

网站开发案例课堂:HTML5+CSS3+JavaScript网页设计案例课堂

刘玉红 / 2015-1-1 / 68

《网站开发案例课堂:HTML5+CSS3+JavaScript网页设计案例课堂》作者根据在长期教学中积累的网页设计教学经验,完整、详尽地介绍HTML 5 + CSS 3 + JavaScript网页设计技术。 《网站开发案例课堂:HTML5+CSS3+JavaScript网页设计案例课堂》共分24章,分别介绍HTML 5概述、HTML 5网页文档结构、HTML 5网页中的文本和图像、HTML......一起来看看 《网站开发案例课堂:HTML5+CSS3+JavaScript网页设计案例课堂》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器