为什么我不能在Java枚举上使用foreach?


70

我为什么不能做:

Enumeration e = ...
for (Object o : e)
  ...

4
枚举在1998年被Java 1.2中的Iterator取代,并保留为旧版支持。真正的问题是为什么要使用它。可能是因为您使用了强迫您这样做的库?
彼得·劳瑞

2
@Peter-是的,答案是外部库。
ripper234

Answers:


63

因为Enumeration<T>不扩展Iterable<T>。这是进行Iterable Enumerations示例

至于为什么这是一个有趣的问题。这不完全是您的问题,但却为您提供了一些启示。来自Java Collections API设计常见问题解答

为什么Iterator不扩展枚举?

我们认为不幸的是Enumeration的方法名称。它们很长,而且经常使用。鉴于我们要添加一种方法并创建一个全新的框架,我们认为不利用机会来改进名称是愚蠢的。当然,我们可以在Iterator中支持新名称和旧名称,但这似乎并不值得。

这基本上向我表明Sun希望与Enumeration保持距离,Enumeration是非常早期的Java,具有相当冗长的语法。


21
下一个问题:为什么枚举不是可迭代的?stackoverflow.com/questions/27240/…–
迈克尔·迈尔斯

1
两者在语义上有区别吗?(我对Iterable类不熟悉)?为什么不同时进行枚举呢?
ripper234

4
由于枚举更类似于Iterator,而不是Iterable(请参见发布的stackoverflow问题)。解决方法是,您可以for(; e.hasMoreElements(); ) { e.getNextElement(); }
蒂姆(Tim)2009年

4
Iterators两者都不Iterable是。你做不到for (obj : itr)。这是因为Iterable它实际上是可重复迭代的,而迭代器只能迭代一次。
oxbow_lakes

4
这并不能解释为什么Sun / OracleEnumeration在新for语法中不允许使用anIterable和an array
mjaggard 2013年

45

使用Collections实用程序类,可以使Enumeration变得可迭代,如下所示:

Enumeration headerValues=request.getHeaders("mycustomheader");
List headerValuesList=Collections.list(headerValues);

for(Object headerValueObj:headerValuesList){
 ... do whatever you want to do with headerValueObj
}

4
在开始迭代之前,将元素复制到新列表中,对于长时间枚举,应避免使用此解决方案。将枚举转换为迭代器的解决方案具有较少的内存开销。
伊曼纽尔·布尔格

@EmmanuelBourg,但它们有副作用,因为枚举是有状态的。
P.Péter

1
到目前为止,这是最简单的答案!无需依赖项,并且可读性强。谢谢!
Geert Schuring

7

我已经通过两个非常简单的类(一个用于Enumeration和一个用于)解决了这个问题Iterator。枚举包装器如下:

static class IterableEnumeration<T>
extends Object
implements Iterable<T>, Iterator<T>
{
private final Enumeration<T>        enumeration;
private boolean                     used=false;

IterableEnumeration(final Enumeration<T> enm) {
    enumeration=enm;
    }

public Iterator<T> iterator() {
    if(used) { throw new IllegalStateException("Cannot use iterator from asIterable wrapper more than once"); }
    used=true;
    return this;
    }

public boolean hasNext() { return enumeration.hasMoreElements(); }
public T       next()    { return enumeration.nextElement();     }
public void    remove()  { throw new UnsupportedOperationException("Cannot remove elements from AsIterator wrapper around Enumeration"); }
}

可以与静态实用程序方法一起使用(这是我的偏爱):

/**
 * Convert an `Enumeration<T>` to an `Iterable<T>` for a once-off use in an enhanced for loop.
 */
static public <T> Iterable<T> asIterable(final Enumeration<T> enm) {
    return new IterableEnumeration<T>(enm);
    }

...

for(String val: Util.asIterable(enm)) {
    ...
    }

或通过实例化该类:

for(String val: new IterableEnumeration<String>(enm)) {
    ...
    }

3

新样式的循环(“ foreach”)适用于数组以及实现Iterable接口的事物。

它也Iterator比更加相似Iterable,因此,Enumeration除非Iterator也这样做,否则与foreach一起工作是没有意义的。 Enumeration也不鼓励Iterator


1
我们在这里要小心用词。在讨论API时,过时意味着非常具体的东西。不鼓励使用Enumeration,但不建议弃用。
ykaganovich

ykaganovich:好点。我已经纠正了答案中的文字。
劳伦斯·贡萨尔维斯

2

使用Java 8或更高版本可以实现:

import java.util.Collections;
import java.util.Enumeration;

Enumeration e = ...;
Collections.list(e).forEach(o -> {
    ... // use item "o"
});

1

Enumeration没有实现Iterable,因此不能直接在foreach循环中使用。但是,使用Apache Commons Collections可以用以下方法遍历枚举:

for (Object o : new IteratorIterable(new EnumerationIterator(e))) {
    ...
}

您也可以使用较短的语法,Collections.list()但这会降低效率(对元素进行两次迭代并增加内存使用量):

for (Object o : Collections.list(e))) {
    ...
}

您是说IteratorIterable吗?
luckyluke

0

因为Enumeration(以及从该接口派生的大多数类)没有实现Iterable。

您可以尝试编写自己的包装器类。


2
您不应该为Enumeration写一个包装器,而将它变成一个Iterable,而不是为Iterator创建这样的包装器。关于迭代,Iterator和Enumeration对象是有状态的。可迭代项不是。
劳伦斯·贡萨尔维斯

0

我们可以使用for循环使用来遍历枚举 .values​​()

method which returns all the elements contained in the enumeration.

一个例子 :

for (USERS_ROLES userRole: USERS_ROLES .values ​​()) {
            System.out.println ("This is one user role:" + userRole.toString ());
}

我在Java 10中做到了

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.