我遇到了一个奇怪的问题。 我以为这会花费我几分钟,但我现在正在努力几个小时...... 这是我得到的:
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个,只会删除少数几个。
我做了一些测试,我也看到了这个:
当我将字符串重命名为: Hardi1 Hardi2 Hardi3 Hardi4 Hardi5 Hardi6
然后它只删除偶数(1,3,5等)。 他一直在跳过1,但无法弄清楚原因。
如何解决这个问题?或者可能是另一种方法来删除它们?
答案 0 :(得分:41)
这里的问题是你正在迭代从0到大小并且在循环内你正在删除项目。删除项目将减小列表的大小,当您尝试访问大于有效大小的索引(删除项目后的大小)时,列表将失败。
有两种方法可以做到这一点。
如果您不想处理索引,请使用迭代器删除。
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);
}
}
答案 1 :(得分:20)
迭代时,不应从列表中删除项目。相反,请使用Iterator.remove()
,如:
for (Iterator<Object> it = list.iterator(); it.hasNext();) {
if ( condition is true ) {
it.remove();
}
}
答案 2 :(得分:12)
每次删除项目时,您正在更改其前面的索引(因此当您删除列表[1]时,列表[2]变为列表[1],因此跳过。
这是一个非常简单的方法:(倒数而不是上升)
for(int i = list.size() - 1; i>=0; i--)
{
if(condition...)
list.remove(i);
}
答案 3 :(得分:4)
如果你仔细考虑,这是完全合理的。假设您有一个列表[A, B, C]
。第一次通过循环,i == 0
。您会看到元素A
,然后将其删除,因此列表现在为[B, C]
,元素0为B
。现在,您在循环结束时递增i
,因此您正在查看list[1]
C
。
一种解决方案是在删除项目时递减i
,以便“抵消”后续增量。正如亚光b指出的更好的解决方案是使用具有内置Iterator<T>
功能的remove()
。
一般来说,当面对这样的问题时,提出一张纸并假装你是计算机是个好主意 - 完成循环的每一步,写下所有变量就像你一样走。这样就可以“跳过”了。
答案 4 :(得分: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 :(得分: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)
(粗略地)。因此对移除操作的影响非常大。
答案 6 :(得分: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();
}
}
答案 7 :(得分:3)
我不明白为什么这种解决方案对大多数人来说是最好的。
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();
}
}
答案 8 :(得分:2)
这是因为删除元素会修改ArrayList
的索引。
答案 9 :(得分:2)
因为删除值后索引不再好了
此外,您将无法转到size
,因为如果您删除了一个元素,其大小已更改。
您可以使用iterator
来实现这一目标。
答案 10 :(得分:2)
除现有答案外,您还可以使用带有条件增量的常规while循环:
int i = 0;
while (i < data.size()) {
if (data.get(i).getCaption().contains("_Hardi"))
data.remove(i);
else i++;
}
请注意,每次在循环条件中都必须调用data.size()
,否则您最终会得到IndexOutOfBoundsException
,因为删除的每个项目都会改变列表的原始大小。
答案 11 :(得分: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
答案 12 :(得分: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
答案 13 :(得分:1)
这是使用Arraylists时的常见问题,并且由于Arraylist的长度(大小)可能发生变化而发生。删除时,大小也会改变;所以在第一次迭代之后,你的代码变得混乱。最好的建议是使用Iterator或从后面循环,我会建议使用后缀循环,因为我认为它不那么复杂,它仍然适用于众多元素:
//Let's decrement!
for(int i = size-1; i >= 0; i--){
if (data.get(i).getCaption().contains("_Hardi")){
data.remove(i);
}
}
仍然是你的旧代码,只是以不同的方式循环!
我希望这会有所帮助......
快乐编码!!!