为什么Vector方法Iterator和ListIterator快速失败

时间:2010-12-18 18:57:44

标签: java collections

根据http://download.oracle.com/javase/1.4.2/docs/api/java/util/Vector.html

  

Vector的回复迭代器   iterator和listIterator方法是   失败快速:如果Vector是   随时进行结构修改   在Iterator创建之后,任何一个   除了通过Iterator自己的方式   删除或添加方法,迭代器   会抛出一个   ConcurrentModificationException的。从而,   面对并发   修改,迭代器失败   快速而干净,而不是   冒着任意,不确定的风险   在一个不确定的时间的行为   未来。枚举返回   通过Vector的元素方法不是   快速失败的。请注意,失败快   迭代器的行为不可能   通常保证原样   说话,不可能做出任何努力   保证存在   不同步的并发   修改。失败快速的迭代器   抛出ConcurrentModificationException   尽力而为。因此,它   写一个程序是不对的   依赖于这个例外   正确性:失败的快速行为   迭代器应该只用于   检测错误

你能给我一个例子来验证上面的语句吗?我还不清楚vector的方法Iterator和ListIterator的快速行为失败。困惑: - ((

3 个答案:

答案 0 :(得分:8)

  

如果在创建迭代器后的任何时候对Vector进行结构修改,除了通过Iterator自己的remove或add方法之外,Iterator将抛出ConcurrentModificationException

以下是一个例子:

import java.util.*;

public class Test {

    public static void main(String[] args) {
        List<String> strings = new Vector<String>();

        strings.add("lorem");
        strings.add("ipsum");
        strings.add("dolor");
        strings.add("sit");

        int i = 0;

        Iterator<String> iter = strings.iterator();
        while (iter.hasNext()) {
            System.out.println(iter.next());

            // Modify the list in the middle of iteration.
            if (i++ == 1)
                strings.remove(0);
        }
    }
}

输出:

lorem
ipsum
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
    at java.util.AbstractList$Itr.next(AbstractList.java:343)
    at Test.main(Test.java:18)

该计划执行以下操作:

  1. 创建一个Vector并获取一个迭代器
  2. 两次致电next()
  3. 修改向量(通过删除第一个元素)
  4. 再次调用next()(修改了矢量后)
  5. 这会导致ConcurrentModificationException被抛出。
  6. 由于Java的for-each循环依赖于迭代器,因此这些构造也可能抛出ConcurrentModificationExceptions。解决方案是在迭代之前制作列表的副本(以便迭代副本)或者使用例如CopyOnWriteArrayList这样的:

    import java.util.*;
    import java.util.concurrent.CopyOnWriteArrayList;
    
    public class Test {
    
        public static void main(String[] args) {
            List<String> strings = new CopyOnWriteArrayList<String>();
    
            strings.add("lorem");
            strings.add("ipsum");
            strings.add("dolor");
            strings.add("sit");
    
            int i = 0;
    
            Iterator<String> iter = strings.iterator();
            while (iter.hasNext()) {
                System.out.println(iter.next());
    
                // Modify the list in the middle of iteration.
                if (i++ == 1)
                    strings.remove(0);
            }
        }
    }
    

    输出:

    lorem
    ipsum
    dolor
    sit
    

答案 1 :(得分:2)

触发并发修改异常的一种简单方法是

List<String> strings = new ArrayList<String>();
strings.add("a");
strings.add("b");
for(String s: strings)
  strings.remove(s);

这会触发异常,因为在对集合进行迭代时会更改集合。

Iterator快速失败的原因是帮助您检测到并发修改了一个集合(这些集合不支持)并帮助检测错误发生的位置。如果它没有此功能,您可能会遇到一些细微的错误,这些错误可能会在您的代码中稍后出现问题。 (让他们更难以参加比赛)

较新的并发集合以不同方式处理并发修改,并且通常不执行此操作。他们在2004年被引入核心Java,我建议你看看这些新的系列。

BTW:除非必须,否则不要使用Vector。

答案 2 :(得分:1)

假设您有一个包含1-10的整数向量,并且您想删除奇数。您遍历此列表以查找赔率并使用迭代器remove()方法。在此之后你有一些代码,当然假设Vector中没有奇数。如果另一个线程在此过程中修改了向量,有时可能实际上存在奇数(取决于竞争条件),从而破坏了之后的代码。也许它甚至没有立即破裂;也许它不会在几小时或几天后引起问题 - 很难排除故障。这就是elements()方法所发生的情况。

快速失败意味着一旦发生并发出警报就会尝试检测此(潜在)问题,这样可以更容易地进行故障排除。一旦发现另一个线程修改了集合,就会抛出异常。这就是迭代器所发生的事情。

iterator()listIterator()返回的迭代器主动监视对基础列表的意外修改。 Vector类(实际上是它的父AbstractList)在每次修改时递增一个计数器。当创建Vector的迭代器时,它们存储Vector的修改计数器的副本。每次调用next()remove()时,迭代器都会将计数器的存储值与Vector的实际计数器进行比较。如果它们不同,则会抛出ConcurrentModificationException。