首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java中的并发修改

Java中的并发修改
EN

Stack Overflow用户
提问于 2014-07-17 06:04:13
回答 5查看 140关注 0票数 1

如果我写

代码语言:javascript
复制
String myFirstString = "a";
String mySecondString = "b";

List<String> lstOfStrings = new ArrayList<String>();
lstOfStrings.add(myFirstString);
lstOfStrings.add(mySecondString);

for (String value : lstOfStrings) {
    if(value.equals("a")) {
        lstOfStrings.remove("a");
        System.out.println("removed successfully");
    }
}

很好,但是,

如果我更改列表中插入的顺序,它将给java.util.ConcurrentModificationException提供如下代码

代码语言:javascript
复制
String myFirstString = "a";
String mySecondString = "b";

List<String> lstOfStrings = new ArrayList<String>();
lstOfStrings.add(mySecondString);
lstOfStrings.add(myFirstString);

for (String value : lstOfStrings) {
    if(value.equals("a")) {
        lstOfStrings.remove("a");
        System.out.println("removed successfully");
    }
}

给出java.util.ConcurrentModificationException

为什么每个人都会有这样的行为?我知道有许多方法像Iterator,CoppyOnWriteArraylist作为ConcurrentModificationException异常的解决方案。但我想知道这个案子的原因。请解释一下。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2014-07-17 06:16:36

我将猜测,为for (val: collection )的迭代器语法糖生成的字节代码的实现包括优化检查您是否在集合的最后一个元素上,而不需要再次进入迭代器。因此,如果您移除集合中最后一个项,它将不会抛出异常并跳过最后一个项。这在一定程度上被两个实验所证实:在你的第一个例子中添加第三项,它就会失败。修改它以删除第二项而不是第一项,并将再次完成“成功”。

更新:啊,这个问题是Why isn't this code causing a ConcurrentModificationException?的翻版

票数 3
EN

Stack Overflow用户

发布于 2014-07-17 06:07:43

Java循环内部使用集合的iterator

iterator的行为是fail-fast,当集合的任何内容在循环过程中被更改时,它都会失败。

有关更多信息,我建议阅读下面的文章。

http://www.jguru.com/faq/view.jsp?EID=221988

http://www.developersfusion.com/Articles/AD/F/53/ConcurrentModificationException---Fail-Fast-and-Fail-Safe-iterators.aspx

http://docs.oracle.com/javase/6/docs/api/java/util/ConcurrentModificationException.html

票数 3
EN

Stack Overflow用户

发布于 2014-07-17 06:29:45

正是由于ArrayList迭代器的实现方式。

当在最后一个元素之前删除元素时,迭代器将不再迭代最后一个元素。

试着打印出每一个迭代

代码语言:javascript
复制
String myFirstString = "a";
String mySecondString ="b";
List<String> lstOfStrings = new ArrayList<String>();

lstOfStrings.add(myFirstString);
lstOfStrings.add(mySecondString);

for (String value : lstOfStrings) {
    System.out.println("Iterating: " + value);

    if(value.equals("a")){
        lstOfStrings.remove("a");
        System.out.println("removed successfully");
    }
}

System.out.println(lstOfStrings);

输出是

代码语言:javascript
复制
Iterating: a
removed successfully
[b]

正如您在最后一个System.out.println中看到的那样,lstOfString仍然包含元素"b",但它并不是迭代的。

当您查看ArrayList的实现时,您可以看到其中的原因。

代码语言:javascript
复制
private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    public boolean hasNext() {
        return cursor != size;
    }
    ....
}

Iterator.hasNext()只检查实际游标是否为大小。因此,如果迭代列表并删除字符串"a",光标指向第二个元素"b",但是大小已经缩小到1,因此第二个元素不再迭代。

甚至在JDK1.7中也会发生这种情况,而且我还没有测试这种行为是否在1.8中仍然存在。

当您使用Iterator.remove()方法时,它将工作,因为迭代器知道删除。

代码语言:javascript
复制
Iterator<String> iterator = lstOfStrings.iterator();
    while (iterator.hasNext()) {
        String value = iterator.next();
        System.out.println("Iterating: " + value);
        if (value.equals("a")) {
            iterator.remove();
            System.out.println("removed successfully");
        }
    }

打印出来:

代码语言:javascript
复制
Iterating: a
removed successfully
Iterating: b
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24795963

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档