【问题标题】:Delete data from ArrayList with a For-loop使用 For 循环从 ArrayList 中删除数据
【发布时间】:2012-05-31 02:30:41
【问题描述】:

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

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

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

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

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

当我将字符串重命名为: 哈迪1 哈迪2 哈迪3 哈迪4 哈迪5 哈迪6

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

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

【问题讨论】:

    标签: java android arraylist


    【解决方案1】:

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

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

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

    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);
        }
     }
    

    【讨论】:

    • 谢谢你,也谢谢你的解释。现在我也知道为什么它总是跳过了。
    • 如果我像data = new ArrayList&lt;&gt;()一样重新初始化会怎样
    • 从末尾删除部分非常好
    【解决方案2】:

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

    for (Iterator<Object> it = list.iterator(); it.hasNext();) {
        if ( condition is true ) {
            it.remove();
        }
    }
    

    【讨论】:

    • 请注意,it.next() 必须被调用。有无条件。
    【解决方案3】:

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

    这是一个非常简单的方法:(倒计时而不是倒计时)

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

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

        【解决方案5】:

        如果您仔细考虑一下,那将是非常有意义的。假设您有一个列表[A, B, C]。第一次通过循环,i == 0。您会看到元素 A,然后将其删除,因此列表现在为 [B, C],元素 0 为 B。现在你在循环结束时增加i,所以你看到的是list[1],即C

        一种解决方案是每当您删除一个项目时减少i,以便它“取消”随后的增量。正如上面 matt b 所指出的,一个更好的解决方案是使用具有内置 remove() 函数的 Iterator&lt;T&gt;

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

        【讨论】:

          【解决方案6】:

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

          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();
              }
          }
          

          【讨论】:

            【解决方案7】:

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

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

            您可以使用iterator 来实现。

            【讨论】:

              【解决方案8】:
              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)(大致)。因此对删除操作的影响非常大。

              【讨论】:

                【解决方案9】:

                虽然晚了,但可能对某人有用。

                Iterator<YourObject> itr = yourList.iterator();
                
                // remove the objects from list
                while (itr.hasNext())
                {
                    YourObject object = itr.next();
                    if (Your Statement) // id == 0
                    {
                        itr.remove();
                    }
                }
                

                【讨论】:

                  【解决方案10】:

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

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

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

                  【讨论】:

                    【解决方案11】:

                    发生这种情况是因为删除元素会修改ArrayList 的索引。

                    【讨论】:

                      【解决方案12】:
                      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

                      【讨论】:

                        【解决方案13】:

                        有一种更简单的方法可以解决这个问题,而无需创建新的迭代器对象。这是概念。假设您的 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
                        

                        【讨论】:

                          【解决方案14】:

                          这是使用 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);
                              }
                           }
                          

                          还是你的旧代码,只是循环方式不同!

                          我希望这会有所帮助...

                          编码愉快!!!

                          【讨论】:

                            猜你喜欢
                            • 2021-07-04
                            • 2014-11-13
                            • 2021-05-21
                            • 2019-11-03
                            • 1970-01-01
                            • 2015-10-06
                            • 1970-01-01
                            • 2018-03-27
                            相关资源
                            最近更新 更多