37

HttpServletRequest is using a lot of java.util.Enumeration. I would like to use them in for-each, so i need to convert them into interable. this is not a problem, but I since I have more than one project needing this I need a library to do this. I would rather not make my own - is there any standard library that supports this kind of decoration?

Is there a built-in construct to convert an Enumeration to an Iterable?

Leonel
  • 28,541
  • 26
  • 76
  • 103
IAdapter
  • 62,595
  • 73
  • 179
  • 242
  • 2
    It would also be nice to have Iterator->Iterable without the O(n) cost for converting to a list. – Michael Deardeuff Nov 19 '11 at 22:44
  • 1
    Added an enhancement ticket to google guava: http://code.google.com/p/guava-libraries/issues/detail?id=796 – Michael Deardeuff Nov 19 '11 at 23:06
  • @Micheal, IAdapter - as can be seen by the javadoc I quoted they have no intention to do so. Also the ticket has been referred to the [idea graveyard](http://code.google.com/p/guava-libraries/wiki/IdeaGraveyard), I'll give the gist of the explanation in my answer – Asaf Nov 20 '11 at 02:30
  • As a funny extra: `HttpServletResponse` does the much more common Collection interface. – Thirler Feb 23 '15 at 13:12
  • 1
    I don't agree that this is off topic - the questioner is asking for a solution which may or may not be a library – AutomatedMike Jun 08 '18 at 07:57
  • In Java 9, a new default method [`Enumeration.asIterator()`](https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/Enumeration.html#asIterator()) was added. You can use it in for each loop like this `for (String str : (Iterable) enumeration::asIterator) { } ` – Venkata Raju Jul 03 '21 at 16:03

5 Answers5

49

java.util.Collections has a list method that copies an Enumeration into a List, which you can then use in a for-each loop (see javadoc).

skaffman
  • 398,947
  • 96
  • 818
  • 769
16

Here is the javadoc from Guava regarding the conversion of an Enumeration to Iterator (not iterable):

public static UnmodifiableIterator forEnumeration(Enumeration enumeration)

Adapts an Enumeration to the Iterator interface.

This method has no equivalent in Iterables because viewing an Enumeration as an Iterable is impossible. However, the contents can be copied into a collection using Collections.list(java.util.Enumeration).

Further more apache commons collections current implementation doesn't support Java 5 features and APIs such as Iterable, so there's no chance there.

There are however some methods in those libraries which allow you to change an enumeration to a collection which is iterable and use that (they do implicitly copy your data though).

For instance, transform to a list with EnumerationUtils.toList(enumeration).

Edit: Due to some queries in the question, I'll try and summarize why the makers of guava (and I) don't feel an enumeration can be made into an iterable.

An iterable creates iterator instances, someone reading the code (or API) can assume that each call to iterator() yields a new iterator instance starting the enumeration from the first element. If we do a simple conversion between an iterator (or enumeration) and an iterable then the user of the API needs to know that a call to iterator() changes the state of the object and that 2 consecutive calls might behave oddly. here is an example:

Iterable<String> iter = magicFunction(enumeration);
for (String s : iter) {
  if ("hello".equals(s))
    break;
}

for (String s : iter) {
  if ("world".equals(s))
    return true;
}
return false;

If implemented as a simple conversion ( O(1) ) The above method behaves differently for different inputs: ["hello","world"] would return true, while ["world","hello"] would return false. This is not immediately apparent when looking at the code and can be the cause for many frustrating bugs. Therefore, I believe it makes sense to not have this utility method exist.

Aldo
  • 536
  • 5
  • 23
Asaf
  • 6,384
  • 1
  • 23
  • 44
  • 7
    The silly thing about Guava's "impossible" claim is that we have in fact implemented this "impossible" Iterable in our utility methods. Since we call the method toSingleUseIterable(), it's pretty obvious to the caller that it will only work once. – Hakanai May 17 '12 at 22:25
  • The Iterable returned by the magic function could cache the output and only continue with enumeration.next() when the iteration had passed the cached index. – Carl G Nov 14 '13 at 23:11
  • 1
    that is true, that would save the cost of O(n) on each call to the method and replace it with O(n) memory overhead. the main downside is that you would need the cache to be shared across instances of the iterator which can lead to synchronization overhead or potential bugs, alternatively you can copy the cache to any instance of the iterator you build but than you might as well just copy the whole enumeration and be done with it – Asaf Nov 15 '13 at 12:55
  • 1
    In Java 8 you can use: `Iterators.forEnumeration(oldEnumeratorMethod()).forEachRemaining(e -> {…});` – Jesse Glick Sep 14 '17 at 17:56
8

In my opinion the documented way is the simplest so there is no need to converting into Iterator:

https://docs.oracle.com/javase/1.5.0/docs/api/java/util/Enumeration.html

for (Enumeration<E> e = v.elements(); e.hasMoreElements();)
   System.out.println(e.nextElement());
Krauss
  • 998
  • 10
  • 17
5

Take a look on this article: http://www.java2s.com/Code/Java/Collections-Data-Structure/TreatanEnumerationasanIterable.htm

It seems exactly what you need.

UPDATED Added the code for future reference in case the link would become broken.

import java.util.Enumeration;
import java.util.Iterator;

/**
 * @since 4.37
 * @author Jaroslav Tulach
 */
public class Utils {
  public static <E> Iterable<E> iterable(final Enumeration<E> enumeration) {
      if (enumeration == null) {
          throw new NullPointerException();
      }
      return new Iterable<E>() {
          public Iterator<E> iterator() {
              return new Iterator<E>() {
                  public boolean hasNext() {
                      return enumeration.hasMoreElements();
                  }
                  public E next() {
                      return enumeration.nextElement();
                  }
                  public void remove() {
                      throw new UnsupportedOperationException();
                  }
              };
          }
      };
  }    
}
raisercostin
  • 8,777
  • 5
  • 67
  • 76
AlexR
  • 114,158
  • 16
  • 130
  • 208
  • 3
    no, that is not what I need at all. as I said its easy to write this code and thats not what I want. – IAdapter Nov 19 '11 at 22:27
  • @IAdaptor Can you give more info? Why isn't that what you want? – mjaggard Jun 10 '13 at 11:43
  • 2
    The problem with that code is that all iterators created through the Iterable will consume the same enumerator. – raisercostin Jun 11 '15 at 11:41
  • @raisercostin, IMHO it is not a problem at all. `Iterable` is a interface that defines method `Iterator` that can return different iterator on each invocation. If you wrap this iterator with something that implements interface `Enumeration` you work with iterator returned from `iterator()`. – AlexR Jun 11 '15 at 11:45
  • In the code from the article isn't it true that all Iterators created by the Iterable will consume from the same Enumeration? – raisercostin Jun 11 '15 at 11:54
  • @raisercostin, read the code: ` new Iterator()` does the job. – AlexR Jun 11 '15 at 12:25
  • 1
    Unfortunately if you create two new iterators from the same enumerator as here http://ideone.com/4BxbiM , the iteration on first will consume the base enumerator and will leave nothing for the second iterator. – raisercostin Jun 11 '15 at 13:54
  • This code contains bug (you can't re-use enumeration), test: Vector oldCollection = new Vector<>(Collections.singleton("test")); Iterable iterable = iterable(oldCollection.elements()); assertEquals("test", iterable.iterator().next()); assertEquals("test", iterable.iterator().next()); – 4ndrew Aug 13 '15 at 10:31
2

You have two solutions:

First is to consume the enumerator into a collection that is Iterable(as described by @skaffman). This will add O(n) as noted by @Michael

In cases that you have access to the method that created the enumerator you can wrap this into an Iterable without paying the cost. In your case you can create a Iterable with:

final Request request2 = request;
Iterable<String> headerNamesIterable = new Iterable<String>(){
    public Iterator<T>  iterator(){
      return Iterators.forEnumeration(request2.getHeaderNames())
    }
}
Iterable<String> headersIterableName1 = new Iterable<String>(){
    public Iterator<T>  iterator(){
      return Iterators.forEnumeration(request2.getHeaders("name1"))
    }
}

In this cases you can iterate multiple times on the Iterators created by the Iterable objects.

raisercostin
  • 8,777
  • 5
  • 67
  • 76