我有一个第三方库,它给了我一个Enumeration<String>
。我希望懒惰地使用该枚举作为Java 8 Stream
,在其上调用filter
,map
和flatMap
之类的内容。
是否有现有的库?我已经引用了Guava和Apache Commons,所以如果其中任何一个都有理想的解决方案。
或者,在保留所有内容的懒惰性质的同时,将Enumeration
转换为Stream
的最佳/最简单方法是什么?
答案 0 :(得分:76)
为什么不使用vanilla Java:
Collections.list(enumeration).stream()...
然而,正如@MicahZoltu所提到的,必须考虑枚举中的项目数,因为Collections.list
将首先遍历枚举以复制ArrayList
中的元素。从那里可以使用常规的stream
方法。虽然这对于许多集合流操作来说是常见的,但如果枚举太大(如无限),这可能会导致问题,因为枚举必须在列表中进行转换,然后应该使用此处描述的其他方法。
答案 1 :(得分:36)
This answer已经提供了一个解决方案,可以在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 ......
答案 2 :(得分:23)
在Java 9中,可以使用单行转换Enumeration
到Stream
:
Enumeration<String> en = ... ;
Stream<String> str = StreamSupport.stream(
Spliterators.spliteratorUnknownSize(en.asIterator(), Spliterator.ORDERED),
false
);
(嗯,这是一条相当长的路线。)
如果您不使用Java 9,则可以使用Holger's answer中提供的技术手动将Enumeration
转换为Iterator
。
答案 3 :(得分:8)
根据Guava docs,您可以使用Iterators.forEnumeration()
方法:
Enumeration<Something> enumeration = ...;
Iterator<SomeThing> iterator = Iterators.forEnumeration(enumeration);
在this question中,解释了如何从迭代器获取流:
Stream<Something> stream = StreamSupport.stream(
Spliterators.spliteratorUnknownSize(
iterator, Spliterator.ORDERED),
false);
答案 4 :(得分:3)
在我的StreamEx库中,有一个简单的方法StreamEx.of(Enumeration)
可以完成这项任务:
Stream<String> stream = StreamEx.of(enumeration);
请注意,它不仅是@Holger解决方案的快捷方式,而且以不同的方式实现。特别是,与涉及Spliterators.spliteratorUnknownSize()
的解决方案相比,它具有明显更好的并行执行特性。