108

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?

3
  • 1
    related: Iterate an Enumeration in Java 8 Commented Oct 20, 2015 at 17:07
  • 1
    The question linked is asking how to turn an Enumeration (Java 1.0) into an Iterator (Java 1.2). I'm asking how to turn it into a Stream (Java 1.8). While it does appear that the the last answer in the linked question answers this, that answer is wrong for the question being asked. That answer should be provided here so that future searchers can successfully find it. Perhaps @ArneBurmeister would like to copy the answer here so this question is answered directly? Commented Oct 20, 2015 at 17:19
  • 4
    Reopened as the answers of the linked question do not discuss the lazy behavior and it also wouldn’t be the right place to post alternative ways to create a Stream (as that’s not the scope of the linked question). Commented Oct 20, 2015 at 18:06

5 Answers 5

203

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 first (with a huge backing array), which may have impacts on the memory.

In this case the other approaches described in other answers should be used instead.

E.g. use the great StreamEx library, has it will iterate on the enumeration as needed see Tagir Valeev's answer.

Sign up to request clarification or add additional context in comments.

2 Comments

That will enumerate the entire enumeration, to make it a list and then give you stream access to the list. If the enumeration is small, this probably is a reasonable approach. If the enumeration is very large this could be an expensive and unnecessary operation. If the enumeration is infinite, this will crash your application.
@MicahZoltu Indeed. That is a point to consider, I'll update the answer. Thanks
65

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.

3 Comments

Good stuff Holger. What are the advantages of using Spliterator.ORDERED here vs other values?
@IcedDante ORDERED means that there is a defined encounter order, so it implies that the Stream implementation is not allowed to do optimizations based on assuming the data to be unordered. Since for an unknown Enumeration, we don’t know whether the order has a meaning, we must assume that it could have a meaning and specify this characteristic. The caller still may call unordered() on the stream when they know that the order is irrelevant for the particular data, to enable optimizations. But our initial premise must be that the order could be important.
Note that there is Enumeration#asIterator() since java 9.
61

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.

1 Comment

We can make almost everything a one-liner if we make the line long enough ;^)
14

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

3 Comments

Although it may work in this case, that iterable is not really an Iterable: you can iterate it exactly once!
@dfogni The same happens with streams. Use and discard, they say :)
In context it is perfectly ok, but it is a bug waiting to happen because it declares an object with different semantics than what its interface say. More importantly it is IMHO an abuse of the Iterable interface, which by chance happens to be assignable from a lambda, but is not supposed to be an @FunctionalInterface
10

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().

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.