Java 枚举与迭代器

标签 java

Enumeration 不会抛出 ConcurrentModificationException ,为什么?

见下面的代码。

public static void main(String[] args) {

    Vector<String> v=new Vector<String>();
    v.add("Amit");
    v.add("Raj");
    v.add("Pathak");
    v.add("Sumit");
    v.add("Aron");
    v.add("Trek");

    Enumeration<String> en=v.elements();

    while(en.hasMoreElements())
    {
        String value=(String) en.nextElement();
        System.out.println(value);
        v.remove(value);

    }

}

它只打印:

Amit
Pathak
Aron

Why is this such behavior . Can we say that Enumerator is thread safe.

Edit: When working with Iterator it throws ConcurrentModificationException in single thread application.

public static void main(String[] args) {

    Vector<String> v=new Vector<String>();
    v.add("Amit");
    v.add("Raj");
    v.add("Pathak");
    v.add("Sumit");
    v.add("Aron");
    v.add("Trek");

    Iterator<String> it=v.iterator();
    while(it.hasNext())
    {
        String value=(String) it.next();
        System.out.println(value);
        v.remove(value);
    }
}

请检查。

最佳答案

请注意,ConcurrentModificationException 与多线程或线程安全意义上的并发无关。有些集合允许并发修改,有些则不允许。通常你可以在文档中找到答案。但是并发并不意味着不同线程并发。这意味着您可以在迭代时修改集合。

ConcurrentHashMap 是一个特例,因为它被明确定义为线程安全的并且在迭代时可编辑(我认为这对所有线程安全的集合都是正确的)。

无论如何,只要您使用单线程迭代和修改集合,ConcurrentHashMap 就是您问题的错误解决方案。您错误地使用了 API。你应该使用 Iterator.remove()删除项目。或者,您可以在迭代和修改原始集合之前制作集合的副本。

编辑:

我不知道有任何枚举会抛出 ConcurrentModificationException。但是,并发修改情况下的行为可能不是您期望的那样。正如您在示例中看到的,枚举会跳过列表中的第二个元素。这是因为无论删除如何,它的内部索引都会增加。所以这就是发生的事情:

  • en.nextElement() - 从 Vector 返回第一个元素,将索引递增到 1
  • v.remove(value) - 从 Vector 中移除第一个元素,将所有元素左移
  • en.nextElement() - 从 Vector 返回第二个元素,现在是“Pathak”

迭代器的快速失败行为可以保护您免受此类事情的影响,这就是为什么它通常比枚举更可取。相反,您应该执行以下操作:

Iterator<String> it=v.iterator();
while(it.hasNext())
{
    String value=(String) it.next();
    System.out.println(value);
    it.remove(); // not v.remove(value); !!
}

或者:

for(String value : new Vector<String>(v)) // make a copy
{
    String value=(String) it.next();
    System.out.println(value);
    v.remove(value);
}

第一个当然更可取,因为只要按预期使用 API,您实际上并不需要副本。

关于Java 枚举与迭代器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10880137/

相关文章:

java - 无法使用方面拦截对方付费调用

java - 如何在Java中高效存储滚动播放项属性?

java - hibernate 多对多关联不更新连接表

java - 如何在JXBrowser 4.1中使用网络摄像头界面

java - 内容类型 "text/xml; charset=utf-8"是错误的吗?

java - 通过 hibernate 获取不同的值

java - 通过 Active Directory 连接到 Azure SQL Server 在 Linux 上部署 Java 应用程序

java - map 和 ImmutableMap 的区别

java - Android:二进制 XML "You must supply a layout_height attribute."

java - 将数组元素转换为新字典中的键的最佳方法