从ArrayList中删除元素

时间:2013-10-15 08:52:40

标签: java arraylist

我正在尝试从ArrayList<String>

中删除某些元素
for(int i=0; i<myList.size(); i++)
{
    if(myList.get(i).contains("foo"))
    {
        myList.remove(i);
    }
}

然而,这会在我的列表中留下“空格”。我希望列表省略空元素,并在迭代后缩小到必要的大小。

有没有一种聪明的方法可以做到这一点,而无需切换到LinkedList

7 个答案:

答案 0 :(得分:32)

  

然而,这会在我的列表中留下“空格”。

不,它没有。它完全从列表中删除条目。其他元素适当移动。 所做的对你所写的方式做的是跳过检查下一个条目...因为那将“拖垮”为元素i,但你会接下来看一下元素i + 1

避免这种情况的一种简单方法是反向工作:

for (int i = myList.size() - 1; i >= 0; i--) {
    if (myList.get(i).contains("foo")) {
        myList.remove(i);
    }
}

或者使用其他答案中提到的迭代器,当然。两者都有效 - 如果您删除多个条目,上面的代码可能会稍微提高效率,因为到达开始时移动的次数会减少。这不太可能是重要的。

不幸的是,为了使用迭代器解决方案,您必须明确地使用迭代器 - 在使用enhanced for loop时无法从集合中删除。

答案 1 :(得分:7)

使用Iterator并改为呼叫Iterator.remove()

Iterator it = myList.iterator();
while(it.hasNext()) {
    if (it.next().contains("foo")) { 
        it.remove();
    }
}

通过这种方式,您还可以避免在减少列表大小的同时将其作为循环的退出条件,并使用可能不同的索引访问它。

当然,向后遍历列表也会有效。

答案 2 :(得分:3)

您正在寻找的智能方式是Iterator界面。例如:

Iterator<String> it = list.iterator();
while (it.hasNext()) {
   String nextItem = it.next();
   if (nextItem.contains("foo")) {
      it.remove();
   }
}

答案 3 :(得分:3)

Scala和函数式编程的影响,我建议您只需将值复制到新列表中以获得不变性。

List<String> filtered = new ArrayList<String>();
for (String s : myList) {
   if (!s.contains("foo")) {
       filtered.add(s);
   }
}

我还建议试用2个库:Guavalambdaj

答案 4 :(得分:2)

ArrayList在场景后面维护一个数组。我想深入了解java.util.ArrayListjava.util.LinkedList

的源代码

首先,ArrayList在幕后维护一个数组。一旦创建了一个ArrayList实例,它就会创建一个大小为10的数组,并在插入元素时增长。尺寸增长到3(尺寸)/ 2 +1

这是源代码。

arrat列表的默认大小。请看constructer code

public ArrayList() {
         this(10);
    }

它的大小增加到3(大小)/ 2 + 1.这里是source codeArrayList#ensureCapacity方法称为insite ArrayList#add

public void ensureCapacity(int minCapacity) {
         modCount++;
         int oldCapacity = elementData.length;
         if (minCapacity > oldCapacity) {
             Object oldData[] = elementData;
             int newCapacity = (oldCapacity * 3)/2 + 1;
             if (newCapacity < minCapacity)
                 newCapacity = minCapacity;
             // minCapacity is usually close to size, so this is a win:
             elementData = Arrays.copyOf(elementData, newCapacity);
        }
     }

从ArrayList中删除任何项目时。它将从列表中删除,其他列表项将向下移动到已删除的项目位置。需要特别注意的是,将此对象的引用设置为null并且对象符合GC的条件,但仍有为ArrayList分配的引用。 ArrayList后面的数组大小相同

以下是source code

public E remove(int index) {
         rangeCheck(index);

        modCount++;
         E oldValue = elementData(index);

         int numMoved = size - index - 1;
         if (numMoved > 0)
             System.arraycopy(elementData, index+1, elementData, index,
                              numMoved);
         elementData[--size] = null; // Let gc do its work

         return oldValue;
     }

正如Jon Skeet所回答的那样,当一个项目被删除时,下一个项目将被移除的项目位于删除的项目位置。

但是,删除后分配的内存空间是相同的。 java.util.LinkedList代表这个问题。 LinkedList中的所有项目都是动态分配和解除分配的(当然是GC的工作)

java.util.LinkedList在幕后维护doubly linked list。每个添加和删除操作都会更改LinkedList使用的内存空间。该项目将被删除,并更新其上一个和下一个项目对项目的引用。

Here is the source code :

private Entry<E> entry(int index) {
        if (index < 0 || index >= size)
           throw new IndexOutOfBoundsException("Index: "+index+
                                                ", Size: "+size);
        Entry<E> e = header;
        if (index < (size >> 1)) {
           for (int i = 0; i <= index; i++)
                e = e.next;
       } else {
            for (int i = size; i > index; i--)
                e = e.previous;
        }
       return e;
   }

我认为GC会在删除后立即收集项目,我知道这不确定。但删除的内存位置是GC的候选者。请注意对象和对象本身的引用。

ArrayList和LinkedList都会删除项目,而ArrayList仍然存储对象类型的引用和基本类型的内存空间,链接列表也会删除引用和内存空间。至少,参考和记忆也有资格进入GC。

答案 5 :(得分:1)

删除列表后会自动缩小。

假设您删除了索引3处的元素,该元素将被删除,list将缩小,并且索引4处的元素在删除后将具有索引3.

你应该这样做:

for(int i=0; i<myList.size(); i++)
{
    if(myList.get(i).contains("foo"))
    {
        myList.remove(i);
        // as element is removed, next element will have decremented index
        i--;
    }
}

答案 6 :(得分:0)

不,它不会在您的列表中留下“空格”,但您将错过从列表中删除所有必需元素。

让我们尝试用下面的例子来解释。 我有一个包含4个元素的ArrayList。 (A,B,C,d)。

for (int i = 0; i < list.size(); i++) {
            if (((String) list.get(i)).contains("c")) {
                list.remove(i);
            }
            if (((String) list.get(i)).contains("b")) {
                list.remove(i);
            }
        }

for (int i = 0; i < list.size(); i++) {
        System.out.print(list.get(i)+" ");
    }

结果:a c d

在向前遍历列表时,我尝试删除元素(c,b),但我的列表中仍然存在元素c。

为了避免这种情况,我们可以像往下一样向后移动。

for (int i = list.size() - 1; i >= 0; i--) {
            if (((String) list.get(i)).contains("a")) {
                list.remove(i);
            }
            if (((String) list.get(i)).contains("c")) {
                list.remove(i);
            }
        }

        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }

结果:b d