繁体   English   中英

如何将Java枚举转换为流?

[英]How do I turn a Java Enumeration into a Stream?

我有一个第三方库,它给我一个Enumeration<String> 我希望懒惰地使用该枚举作为Java 8 Stream ,在其上调用filtermapflatMap的东西。

是否有现有的库? 我已经引用了Guava和Apache Commons,所以如果其中任何一个都有理想的解决方案。

或者,在保留所有内容的惰性的同时,将Enumeration转换为Stream的最佳/最简单方法是什么?

为什么不使用vanilla Java:

Collections.list(enumeration).stream()...

但是,正如@MicahZoltu所提到的,必须考虑枚举中的项目数,因为Collections.list将首先遍历枚举以复制ArrayList的元素。 从那里可以使用常规stream方法。 虽然这对于许多集合流操作来说是常见的,但如果枚举太大(如无限),这可能会导致问题,因为枚举必须在列表中进行转换,然后应该使用此处描述的其他方法。

这个答案已经提供了一个解决方案,它可以从Enumeration创建一个Stream

  public static <T> Stream<T> enumerationAsStream(Enumeration<T> e) { return StreamSupport.stream( Spliterators.spliteratorUnknownSize( new Iterator<T>() { public T next() { return e.nextElement(); } public boolean hasNext() { return e.hasMoreElements(); } }, Spliterator.ORDERED), false); } 

应当强调的是,所产生的Stream 其他任何偷懒Stream ,因为它不会处理任何物品的终端动作已经开始,如果终端操作短路,它会遍历只有尽可能多的项目之前,需要。

不过,它还有改进的余地。 当有一种直接处理所有元素的方法时,我总是会添加一个forEachRemaining方法。 对于大多数非短路操作, Stream实现将调用所述方法:

public static <T> Stream<T> enumerationAsStream(Enumeration<T> e) {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            new Iterator<T>() {
                public T next() {
                    return e.nextElement();
                }
                public boolean hasNext() {
                    return e.hasMoreElements();
                }
                public void forEachRemaining(Consumer<? super T> action) {
                    while(e.hasMoreElements()) action.accept(e.nextElement());
                }
            },
            Spliterator.ORDERED), false);
}

但是,上面的代码是“使用Iterator因为它是如此熟悉的”反模式的受害者。 创建的Iterator将被包装到新Spliterator接口的实现中,并没有直接实现Spliterator优势:

public static <T> Stream<T> enumerationAsStream(Enumeration<T> e) {
    return StreamSupport.stream(
        new Spliterators.AbstractSpliterator<T>(Long.MAX_VALUE, Spliterator.ORDERED) {
            public boolean tryAdvance(Consumer<? super T> action) {
                if(e.hasMoreElements()) {
                    action.accept(e.nextElement());
                    return true;
                }
                return false;
            }
            public void forEachRemaining(Consumer<? super T> action) {
                while(e.hasMoreElements()) action.accept(e.nextElement());
            }
    }, false);
}

在源代码级别,此实现与基于Iterator的实现一样简单,但消除了从SpliteratorIterator的委派。 它只需要读者了解新的API。

在Java 9中,可以使用单行将Enumeration转换为Stream

Enumeration<String> en = ... ;
Stream<String> str = StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(en.asIterator(), Spliterator.ORDERED),
    false
);

(嗯,这是一个相当长的路线。)

如果您不使用Java 9,则可以使用Holger答案中给出的技术手动将Enumeration转换为Iterator

根据Guava文档 ,您可以使用Iterators.forEnumeration()方法:

Enumeration<Something> enumeration = ...;

Iterator<SomeThing> iterator = Iterators.forEnumeration(enumeration);

这个问题中 ,它解释了如何从迭代器获取流:

Stream<Something> stream = StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(
        iterator, Spliterator.ORDERED),
    false);

在我的StreamEx库中有一个简单的方法StreamEx.of(Enumeration)来完成这项工作:

Stream<String> stream = StreamEx.of(enumeration);

请注意,它不仅是@Holger解决方案的快捷方式,而且以不同的方式实现。 特别是,与涉及Spliterators.spliteratorUnknownSize()解决方案相比,它具有明显更好的并行执行特性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM