使用 For 循环从 ArrayList 中删除数据
Posted
技术标签:
【中文标题】使用 For 循环从 ArrayList 中删除数据【英文标题】:Delete data from ArrayList with a For-loop 【发布时间】:2012-05-31 02:30:41 【问题描述】:我遇到了一个奇怪的问题。 我以为这会花费我几分钟,但我现在挣扎了几个小时...... 这是我得到的:
for (int i = 0; i < size; i++)
if (data.get(i).getCaption().contains("_Hardi"))
data.remove(i);
data
是 ArrayList.
在 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,但不知道为什么。
如何解决这个问题?或者也许是另一种删除它们的方法?
【问题讨论】:
【参考方案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<>()
一样重新初始化会怎样
从末尾删除部分非常好【参考方案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<T>
。
一般来说,当遇到这样的问题时,拿出一张纸并假装自己是计算机,这是一个好主意——遍历循环的每一步,写下所有变量走。那会让“跳过”变得清晰。
【讨论】:
【参考方案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);
还是你的旧代码,只是循环方式不同!
我希望这会有所帮助...
编码愉快!!!
【讨论】:
以上是关于使用 For 循环从 ArrayList 中删除数据的主要内容,如果未能解决你的问题,请参考以下文章
ArrayList循环删除报错Exception in thread “main“ java.util.ConcurrentModificationException
一个ArrayList在循环过程中删除,会不会出问题,为什么?
C++ STL容器在for循环中删除迭代器 正确方法 it++正确吗