Java枚举和迭代器的区别

138

这两个接口有什么确切的区别呢?使用 Enumeration 是否比使用Iterator 更有优势?如有详细解释和参考文章将不胜感激。


4
我使用谷歌搜索,第一个结果是JavaRanch上关于Enumeration vs Iterator的一次有趣讨论。 - victor hugo
10个回答

156

查看Java API规范中Iterator接口的说明,其中解释了与Enumeration之间的区别:

迭代器和枚举有两个不同之处:

  • 迭代器允许调用者在迭代期间以明确定义的语义从底层集合中删除元素。
  • 方法名称已得到改进。

总的来说,EnumerationIterator都会提供连续的元素,但是Iterator通过缩短冗长的方法名来改进方法,并增加了一个remove方法。以下是并排比较的示例:

  Enumeration                     Iterator
  ----------------                ----------------
  hasMoreElements()               hasNext()
  nextElement()                   next()
  N/A                             remove()

正如Java API规范中所提到的,对于新程序,应优先使用Iterator而非Enumeration,因为“在Java集合框架中,Iterator取代了Enumeration的位置。”(来自Iterator规范)


9
关于并发性,我认为这个回答缺少一些解释。 - Maarten Bodewes
@Paul_Draper:编辑不应该给帖子添加新的含义,这就是评论的作用。 - Emil
4
@coobird 您确定“枚举通常更快”吗?因为 Enumeration 在 nextElement() 方法内有“同步代码块”,而我们在迭代器中没有同步,这会导致 ConcurrentModificationException 异常,对吧?我们可以称迭代器通常更快,而枚举则稍微更安全一些。 - Kanagavelu Sugumar
1
方法名称已经改善。- 谢谢您的诚实;) - Deqing
请在回答中也加入"fail-fast"的解释! - radistao
显示剩余4条评论

40

迭代器是快速失败的(fail-fast)。即当一个线程通过添加/删除操作更改集合,而另一个线程正在使用hasNext()或next()方法遍历它时,迭代器会快速地抛出ConcurrentModificationException。迭代器的快速失败行为仅用于检测错误。由类似于Hashtable、Vector的类的方法返回的枚举不是快速失败的,这是通过在nextElement()方法内同步代码块来实现的,该代码块锁定当前Vector对象,这需要花费大量时间。


12
只是部分正确:这种行为在接口中没有被定义,它取决于Iterator的实现。 确实,在java.util中的“旧”集合实现(如HashSet、ArrayList等)表现出这种行为。 然而,较新的“并发”集合将永远不会抛出ConcurrentModificationException,它们将按照迭代器创建时的集合状态来遍历集合。 其他实现可能会展示不同的行为。 - Kutzi
1
还值得指出的是:“请注意,由于在未同步的并发修改存在的情况下不可能做出任何硬性保证,因此无法保证快速失败行为。快速失败操作尽力抛出ConcurrentModificationException异常。因此,编写依赖于此异常的程序来确保其正确性是错误的:ConcurrentModificationException仅应用于检测错误。” http://docs.oracle.com/javase/7/docs/api/java/util/ConcurrentModificationException.html - Kutzi

12

“官方”上讲,迭代器接口支持额外操作(例如删除)并且与枚举接口相似。通常倾向于使用迭代器。

下面是来自枚举接口javadoc的内容:

注意:此接口的功能与迭代器接口重复。此外,迭代器还添加了一个可选的remove操作,并且方法名称更短。新实现应优先考虑使用迭代器而不是枚举。


7

枚举(Enumeration)和迭代器(Iterator)有三个区别:

枚举(Enumeration)

  1. 它只用于遗留类(例如Vector)

    // v是Vector类的一个对象
    Enumeration e = v.elements();  
    
  2. 我们不能删除元素,集合本身不能被改变;只能访问元素

  3. 以下两个方法是可用的:

    • public boolean hasNextElement()
    • public Object nextElement()

迭代器

  1. 它适用于集合框架中的所有集合:

    // c 是任何集合
    classIterator itr = c.iterator();  
    
  2. 可以同时迭代和访问元素,并且可以删除元素。

  3. 有三种方法可用

    • public boolean hasNext()
    • public Object next()
    • public void remove()

枚举迭代器限制:

  • 它们只能向前移动;
  • 没有添加或替换元素的方法。

7

之前的回答中没有提到的一个简单事实是,Iterator<T>Iterable<T> 结合使用,用于解释 for(_type_ element:collection){...} 结构。


4

1) 迭代器(Iterator)和枚举(Enumeration)之间的主要区别在于在遍历集合时是否可以删除元素。由于迭代器具有remove()方法,因此在遍历集合期间可以删除元素。而枚举没有remove()方法。

2) 枚举是安全失败的。如果在遍历期间修改了集合,它不会抛出ConcurrentModificationException异常。 迭代器是快速失败的。如果在迭代除自己的remove()方法以外的集合时进行修改,它将抛出ConcurrentModificationException异常。

3) 枚举是一个遗留的接口,用于遍历Vector、Hashtable等类。 迭代器不是遗留接口,可用于HashMap、LinkedList、ArrayList、HashSet、TreeMap、TreeSet等类的遍历。


是的,正如到目前为止所有其他答案所述。 - Maarten Bodewes

2

主要区别在于Enumeration不公开remove()方法。此外,Iterator不允许同时导航和修改基础对象。它们有一个控制器来查看是否存在并发修改等情况,因此需要更多的处理。因此,Enumeration的性能几乎比Iterator快50%。如果我们只需要导航而忽略这种同步,只需使用Enumeration。


确实,枚举类型不会公开remove()方法,但它也不会关注Collection的remove() API调用。例如,以下代码将只打印:AAA,CCC,EEE。
Vector<String> v=new Vector<String>(6); v.add("AAA"); v.add("BBB"); v.add("CCC"); v.add("DDD"); v.add("EEE"); v.add("FFF"); Enumeration<String> en = v.elements(); while(en.hasMoreElements()) String value=(String) en.nextElement(); System.out.println(value); v.remove(value);
- javauser71

2

如果您正在编写自己的集合类,并且您正在扩展任何现有类或实现任何Collections框架接口,则基本上没有选择,只能使用Iterator。

如果您出于某种原因(我想不到)创建了一个与java.util.Collection或java.util.Map没有任何关系的自定义集合类,您仍应该实现Iterable,以便人们可以在for循环中使用您的类。


-1

枚举只能用于旧类(Vector, Stack...),而迭代器可以用于所有类。


-3

迭代器和枚举都用于检索数据,区别在于枚举只能用于旧类,例如vector/stack,而迭代器可以用于其他类。枚举也可用于地图中的键集。


1
你在哪里看到可以使用枚举类型作为Map的键集? - Kutzi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接