I have a third party library that gives me an Enumeration<String>
. I want to work with that enumeration lazily as a Java 8 Stream
, calling things like filter
, map
and flatMap
on it.
Is there an existing library that has this in it? I am already referencing Guava and Apache Commons so if either of those have the solution that would be ideal.
Alternatively, what is the best/easiest way to turn an Enumeration
into a Stream
while retaining the lazy nature of everything?
Using List. stream() method: Java List interface provides stream() method which returns a sequential Stream with this collection as its source.
The Stream. of() and Arrays. stream() are two commonly used methods for creating a sequential stream from a specified array. Both these methods returns a Stream when called with a non-primitive type T.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With