如何将Java枚举转换为流?


75

我有一个第三方图书馆给我一个图书馆Enumeration<String>。我想像Java 8那样懒惰地使用该枚举,并Stream调用诸如此类的东西。filtermapflatMap

有没有现成的图书馆?我已经在引用Guava和Apache Commons,所以如果它们中的任何一个都有理想的解决方案。

另外,在保留所有内容的懒惰性质的同时将Enumerationa变成最佳/最简单的方法Stream是什么?



1
链接的问题是询问如何将Enumeration(Java 1.0)变成Iterator(Java 1.2)。我在问如何将其变成Stream(Java 1.8)。虽然看起来链接的问题中的最后一个答案确实可以回答此问题,但该答案对于所提问题是错误的。应该在此处提供该答案,以便将来的搜索者可以成功找到它。也许@ArneBurmeister想在这里复制答案,以便直接回答这个问题?
米卡·佐尔图

3
由于链接问题的答案没有讨论懒惰的行为,因此重新开放了,它也不是发布替代方法创建Stream链接的正确位置(因为这不是链接问题的范围)。
Holger

Answers:


52

这个答案已经提供了一个解决方案,可以解决Stream以下问题Enumeration

 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基于-一样简单,但是消除了从Spliterator到的委派Iterator。它仅要求其读者学习新的API。


好东西霍尔格。与其他值相比,在此处使用Spliterator.ORDERED有什么优势?
IcedD​​ante

9
@IcedD​​anteORDERED装置,有一个定义遭遇顺序,因此它意味着流实现允许做基于假设数据是无序的优化。由于对于未知数Enumeration,我们不知道命令是否具有含义,因此必须假定其可能具有含义并指定此特征。当调用者unordered()知道顺序与特定数据无关时,他们仍然可以在流上进行调用,以实现优化。但是,我们最初的前提必须是订单可能很重要。
Holger

2
请注意,有Enumeration#asIterator()由于Java 9
dan1st

141

为什么不使用香草Java:

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

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


13
这将枚举整个enumeration,以使其成为列表,然后为您提供对列表的流访问。如果枚举很小,这可能是一个合理的方法。如果枚举很大,则可能是昂贵且不必要的操作。如果枚举是无限的,这将使您的应用程序崩溃。
Micah Zoltu'2

1
@MicahZoltu确实。这是要考虑的一点,我将更新答案。谢谢
布莱斯

我喜欢您的解决方案!
dehasi

39

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

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

(嗯,这是一条很长的线。)

如果您使用的不是Java 9,则可以使用Holger answer中给出的技术EnumerationIterator手动转换为Java 。


24
如果我们使行的长度足够长,我们几乎可以将所有东西都做成单线; ^)
Holger 2015年

12

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

Enumeration<Something> enumeration = ...;

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

这个问题中,将说明如何从迭代器获取流:

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

尽管在这种情况下它可以工作,但是iterable并不是真正的Iterable:您可以将它精确地迭代一次!
dfogni

@dfogni流也是如此。使用和丢弃,他们说:)
fps

在上下文中,这是完全可以的,但这是一个等待发生的错误,因为它声明的对象的语义与其接口所声明的语义不同。更重要的是,恕我直言,它是对Iterable接口的滥用,碰巧是可以从lambda分配的,但不应该是@FunctionalInterface
dfogni 2015年

6

在我的StreamEx库中,有一种简单的方法StreamEx.of(Enumeration)可以完成此工作:

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.