问题
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?
回答1:
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).
回答2:
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.
回答3:
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());
回答4:
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();
}
};
}
};
}
}
回答5:
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.
来源:https://stackoverflow.com/questions/8197797/generic-enumeration-to-iterable-converter