Java列表';s.remove方法只对每个循环中最后一个对象起作用

Java列表';s.remove方法只对每个循环中最后一个对象起作用,java,foreach,Java,Foreach,我看到一种奇怪的行为 List<String> li = new ArrayList<>(); li.add("a"); li.add("b"); li.add("c"); li.add("d"); li.add("e"); for(String str:li){ if(str.equalsIgnoreCase("d")){ li.remove(str); //remo

我看到一种奇怪的行为

    List<String> li = new ArrayList<>();
    li.add("a");
    li.add("b");
    li.add("c");
    li.add("d");
    li.add("e");
    for(String str:li){
        if(str.equalsIgnoreCase("d")){
            li.remove(str);     //removing second last in list works fine
        }
    }
List li=new ArrayList();
li.添加(“a”);
li.添加(“b”);
li.添加(“c”);
li.添加(“d”);
li.添加(“e”);
用于(字符串str:li){
if(str.equalsIgnoreCase(“d”)){
li.remove(str);//删除列表中的倒数第二个很好
}
}
但如果我尝试删除列表中除倒数第二个之外的任何一个,就会得到ConcurrentModificationException。我在阅读《Oracle认证助理Java SE 7程序员学习指南2012》时注意到了这一点,该指南错误地假设.remove()总是与删除列表中最后一个的示例一起使用。

请在循环时从
列表中删除元素时使用
迭代器#remove()
方法。在内部,每个
循环的
将使用
迭代器
列表
中循环,因为如果在迭代过程中以除调用迭代器的remove()以外的任何方式修改基础集合,
迭代器
的行为是未指定的方法。您将获得异常

此循环:

for(String str:li){
    if(str.equalsIgnoreCase("d")){
       li.remove(str);     //removing second last in list works fine
     }
}
基本上

Iterator<String> itr = li.iterator();
  while(itr.hasNext()){
    String str = (String)itr.next();
    if(str.equalsIgnoreCase("d")){
        li.remove(str);     //removing second last in list works fine
    }
}
因此,在这种情况下,
cursor=size=4
,因此
hasNext()
的计算结果为
false
,循环在
next()
中执行并发修改检查之前提前中断。在这种情况下,永远不会访问最后一个元素。如果

    if(str.equalsIgnoreCase("d") || str.equalsIgnoreCase("e")){
        // last element "e" won't be removed as it is not accessed
        li.remove(str);   
    }
但是,如果您删除任何其他元素
next()
,则会调用该元素,从而引发
ConcurrentModificationException

请不要在此处使用
List#删除(对象)
,因为您正在为每个循环访问列表中的元素

改为使用从列表中删除项目:

for(Iterator<String> it=li.iterator(); it.hasNext();) {
    String str = it.next();
    if(str.equalsIgnoreCase("d")) {
        it.remove();     //removing second last in list works fine
    }
}
for(Iterator it=li.Iterator();it.hasNext();){ String str=it.next(); if(str.equalsIgnoreCase(“d”)){ it.remove();//删除列表中的倒数第二个很好 } }
由于ArrayList的fail fast行为引发ConcurrentException。这意味着,除了迭代器#remove()


参考如果你真的想在
数组列表上迭代并删除元素,那么你应该这样做:

for(int index = yourArrayList.size() - 1; index >= 0; index--) {
    if(yourCondition) {
        yourArrayList.remove(index);
    }
}
您可以“向后”迭代并删除元素,但不能向前。因此,与其从第一个元素迭代到最后一个元素,不如从最后一个元素迭代到第一个元素

伪代码:

for(int i = list.size() -1; i >= 0; i--)
{
   list.remove(i);
}

在列表中,添加或删除被视为修改。在你的情况下,你做了 5修改(增补)

“for each”循环的工作原理如下:

3.如果为true,则使用next()获取下一个元素

重复步骤2和3,直到hasNext()返回false

如果我们从列表中删除一个元素,它的大小会减小,modCount会增加

如果我们在迭代时删除一个元素,modCount!=期望的模块数量得到满足 并抛出ConcurrentModificationException

但移除倒数第二个物体很奇怪。让我们看看它在您的案例中是如何工作的

最初,

游标=0 size=5-->hasNext()成功,next()也成功 毫无例外。
游标=1 size=5-->hasNext()成功,next()也成功 毫无例外。
游标=2 size=5-->hasNext()成功,next()也成功 毫无例外。
游标=3 size=5-->hasNext()成功,next()也成功 毫无例外

在您的情况下,当您删除“d”时,大小将减少到4

cursor=4 size=4-->hasNext()未成功,而next()未成功 跳过

在其他情况下,ConcurrentModificationException将作为modCount!抛出预期的模数

在这种情况下,不会进行此检查

如果在迭代时尝试打印元素,则只会打印四个条目。跳过最后一个元素


希望我说得清楚。

如果你想删除所有,那么。removeAll应该做到这一点,而不是遍历集合。我认为它也更快

实际上我知道如何删除数组中的一个元素,但我想指出为什么它只对最后一个元素起作用,这是一个错误吗??
for(int i = list.size() -1; i >= 0; i--)
{
   list.remove(i);
}
1.It gets the iterator.
2.Checks for hasNext().
public boolean hasNext() 
{
      return cursor != size(); // cursor is zero initially.
}
public E next() 
{
        checkForComodification();
        try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
        } catch (IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
        }
}

final void checkForComodification() 
{
    // Initially modCount = expectedModCount (our case 5)
        if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}