20

我遇到了一个奇怪的问题。我以为这会花费我几分钟,但我现在挣扎了几个小时......这是我得到的:

for (int i = 0; i < size; i++){
    if (data.get(i).getCaption().contains("_Hardi")){
        data.remove(i);
    }
}

在ArrayListdataArrayList. 我得到了一些字符串(总共 14 个左右),其中 9 个在其中得到了名称 _Hardi。

使用上面的代码,我想删除它们。如果我replace data.remove(i);用 aSystem.out.println那么它会打印出 9 次,这很好,因为 _Hardi 在 ArrayList 中出现了 9 次。

但是当我使用data.remove(i);then 它不会删除所有 9 个,而只会删除一些。我做了一些测试,我也看到了这个:

当我将字符串重命名为: Hardi1 Hardi2 Hardi3 Hardi4 Hardi5 Hardi6

然后它只删除偶数(1、3、5 等)。他一直在跳过1,但不知道为什么。

如何解决这个问题?或者也许是另一种删除它们的方法?

4

14 回答 14

53

这里的问题是您正在从 0 迭代到 size 并且在循环内您正在删除 items。删除项目将减少列表的大小,当您尝试访问大于有效大小(删除项目后的大小)的索引时,列表将失败。

有两种方法可以做到这一点。

如果您不想处理索引,请使用迭代器删除。

for (Iterator<Object> it = data.iterator(); it.hasNext();) {
if (it.next().getCaption().contains("_Hardi")) {
    it.remove();
}
}

否则,从最后删除。

for (int i = size-1; i >= 0; i--){
    if (data.get(i).getCaption().contains("_Hardi")){
            data.remove(i);
    }
 }
于 2012-05-24T13:48:30.563 回答
20

迭代列表时不应从列表中删除项目。相反,使用Iterator.remove()如下:

for (Iterator<Object> it = list.iterator(); it.hasNext();) {
    if ( condition is true ) {
        it.remove();
    }
}
于 2012-05-24T13:41:22.743 回答
12

每次删除一个项目时,都会更改它前面的项目的索引(因此,当您删除 list[1] 时,list[2] 变为 list[1],因此会跳过。

这是一个非常简单的解决方法:(倒数而不是倒数)


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

于 2012-05-24T13:50:06.767 回答
5

这是因为当您从列表中删除一个元素时,列表的元素会向上移动。因此,如果您删除第一个元素,即索引 0 处的元素,则索引 1 处的元素将移动到索引 0,但您的循环计数器将在每次迭代中不断增加。因此,您没有获得更新的第 0 个索引元素,而是获得了第 1 个索引元素。因此,每次从列表中删除一个元素时,只需将计数器减一即可。

您可以使用以下代码使其正常工作:

for (int i = 0; i < data.size(); i++){
    if (data.get(i).getCaption().contains("_Hardi")){
        data.remove(i);
        i--;
    }
}
于 2013-06-11T11:56:42.623 回答
4

如果你仔细考虑的话,这是完全有道理的。假设你有一个清单[A, B, C]。第一次通过循环,i == 0。您看到元素A,然后将其删除,因此列表现在为[B, C],元素 0 为B。现在你在循环结束时递增i,所以你正在查看list[1]which is C

一种解决方案是i在您删除项目时递减,以便它“取消”随后的递增。正如 matt b 上面指出的,一个更好的解决方案是使用Iterator<T>具有内置remove()函数的 an 。

一般来说,当遇到这样的问题时,拿出一张纸并假装你是计算机是个好主意——遍历循环的每一步,边走边写下所有变量。那将使“跳过”清楚。

于 2012-05-24T13:48:06.863 回答
4

我不明白为什么这个解决方案对大多数人来说是最好的。

for (Iterator<Object> it = data.iterator(); it.hasNext();) {
    if (it.next().getCaption().contains("_Hardi")) {
        it.remove();
    }
}

第三个参数是空的,因为已经移到下一行。此外it.next(),不仅递增循环的变量,而且还用于获取数据。对我来说,使用for循环具有误导性。为什么你不使用while

Iterator<Object> it = data.iterator();
while (it.hasNext()) {
    Object obj = it.next();
    if (obj.getCaption().contains("_Hardi")) {
            it.remove();
    }
}
于 2016-12-22T13:56:30.257 回答
3

除了现有答案之外,您还可以使用带有条件增量的常规 while 循环:

int i = 0;
while (i < data.size()) {
    if (data.get(i).getCaption().contains("_Hardi"))
        data.remove(i);
    else i++;
}

请注意,data.size()必须每次在循环条件中调用,否则您最终会得到一个IndexOutOfBoundsException,因为删除的每个项目都会改变列表的原始大小。

于 2014-08-31T21:50:17.500 回答
3
for (Iterator<Object> it = data.iterator(); it.hasNext();) {
    if ( it.getCaption().contains("_Hardi")) {
        it.remove(); // performance is low O(n)
    }
}

如果您的删除操作在列表中需要很多。最好使用LinkedList,它可以提供更好的性能 Big O(1)(大致)。

ArrayList 的性能在哪里O(n)(大致)。因此对删除操作的影响非常大。

于 2012-05-24T13:48:40.307 回答
3

因为一旦你删除一个值,你的索引就不再好

此外,您将无法访问,size因为如果您删除一个元素,则大小会发生变化。

您可以使用 aniterator来实现。

于 2012-05-24T13:41:10.033 回答
3

为时已晚,但它可能对某人有用。

Iterator<YourObject> itr = yourList.iterator();

// remove the objects from list
while (itr.hasNext())
{
    YourObject object = itr.next();
    if (Your Statement) // id == 0
    {
        itr.remove();
    }
}
于 2015-12-14T16:32:48.723 回答
2

发生这种情况是因为通过删除元素您修改了ArrayList.

于 2012-05-24T13:40:42.590 回答
2

有一种更简单的方法可以解决这个问题,而无需创建新的迭代器对象。这是概念。假设您的 arrayList 包含一个名称列表:

names = [James, Marshall, Susie, Audrey, Matt, Carl];

要从 Susie 中删除所有内容,只需获取 Susie 的索引并将其分配给一个新变量:

int location = names.indexOf(Susie);//index equals 2

现在你有了索引,告诉 java 计算你想从 arrayList 中删除值的次数:

for (int i = 0; i < 3; i++) { //remove Susie through Carl
    names.remove(names.get(location));//remove the value at index 2
}

每次循环值运行时,arrayList 的长度都会减少。由于您已经设置了索引值并且正在计算删除值的次数,因此您已经准备就绪。以下是每次通过后的输出示例:

                           [2]
names = [James, Marshall, Susie, Audrey, Matt, Carl];//first pass to get index and i = 0
                           [2]
names = [James, Marshall, Audrey, Matt, Carl];//after first pass arrayList decreased and Audrey is now at index 2 and i = 1
                           [2]
names = [James, Marshall, Matt, Carl];//Matt is now at index 2 and i = 2
                           [2]
names = [James, Marshall, Carl];//Carl is now at index 3 and i = 3

names = [James, Marshall,]; //for loop ends

以下是您的最终方法的一个片段:

public void remove_user(String name) {
   int location = names.indexOf(name); //assign the int value of name to location
   if (names.remove(name)==true) {
      for (int i = 0; i < 7; i++) {
         names.remove(names.get(location));
      }//end if
      print(name + " is no longer in the Group.");
}//end method
于 2017-02-28T03:11:47.983 回答
2

这是使用 Arraylist 时的常见问题,这是由于 Arraylist 的长度(大小)可以更改而发生的。删除时,大小也会发生变化;所以在第一次迭代之后,你的代码就乱套了。最好的建议是使用 Iterator 或从后面循环,但我会推荐 backword 循环,因为我认为它不那么复杂,并且它仍然适用于许多元素:

//Let's decrement!
for(int i = size-1; i >= 0; i--){
    if (data.get(i).getCaption().contains("_Hardi")){
        data.remove(i);
    }
 }

仍然是您的旧代码,只是循环方式不同!

我希望这有帮助...

编码快乐!!!

于 2017-12-13T21:53:50.710 回答
2
import java.util.ArrayList;

public class IteratorSample {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        ArrayList<Integer> al = new ArrayList<Integer>();
        al.add(1);
        al.add(2);      
        al.add(3);
        al.add(4);

        System.out.println("before removal!!");
        displayList(al);

        for(int i = al.size()-1; i >= 0; i--){
            if(al.get(i)==4){
                al.remove(i);
            }
        }

        System.out.println("after removal!!");
        displayList(al);


    }

    private static void displayList(ArrayList<Integer> al) {
        for(int a:al){
            System.out.println(a);
        }
    }

}

输出:

拆除前!!1 2 3 4

删除后!!1 2 3

于 2016-09-09T08:01:51.573 回答