如何将 Java 枚举转换为流?

我有一个第三方图书馆,给我一个 Enumeration<String>。我希望将这个枚举作为 Java8Stream进行惰性处理,在其上调用诸如 filtermapflatMap之类的内容。

现有的图书馆里有这个吗?我已经引用了 Guava 和 ApacheCommons,因此如果它们中的任何一个有理想的解决方案,那么它们都是理想的。

或者,什么是最好的/最简单的方法把一个 Enumeration变成一个 Stream,同时保持懒惰的性质的一切?

47022 次浏览

This answer already provides a solution which creates a Stream out of an 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);
}

It should be emphasized that the resulting Stream is as lazy as any other Stream, as it won’t process any items before the terminal action has been commenced and if the terminal operation is short-circuiting, it will iterate only as many items as necessary.

Still, it has room for improvement. I’d always add a forEachRemaining method when there is a straight-forward way to process all elements. Said method will be called by the Stream implementation for most non-short-circuiting operations:

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);
}

However, the code above is a victim of the “using Iterator because it’s so familiar” antipattern. The created Iterator will get wrapped into an implementation of the new Spliterator interface and provides no advantage over implementing Spliterator directly:

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);
}

On the source code level, this implementation is as simple as the Iterator-based, but eliminates the delegation from a Spliterator to an Iterator. It only requires its readers to learn about the new API.

According to Guava docs, you could use the Iterators.forEnumeration() method:

Enumeration<Something> enumeration = ...;


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

And in this question, it is explained how to get a stream from an iterator:

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

In Java 9 it is possible to convert an Enumeration to a Stream with a one-liner:

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

(Well, it's a rather long line.)

If you're not on Java 9, you can convert the Enumeration into an Iterator manually using the technique given in Holger's answer.

In my StreamEx library there's simple method StreamEx.of(Enumeration) which does the job:

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

Note that it not just a shortcut to the @Holger solution, but implemented in different manner. In particular, it has significantly better parallel execution characteristics compared to solutions involving Spliterators.spliteratorUnknownSize().

Why not using vanilla Java :

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

However as mentionned by @MicahZoltu, the number of items in the enumeration has to be taken into account, as Collections.list will first iterate over the enumeration to copy the elements in an ArrayList. From there the regular stream method can be used. While this is usual for many collection stream operations, if the enumeration is too big (like infinite), this can cause problem because the enumeration has to be transformed in a list then the other approaches described here should be used instead.