我正在尝试从ArrayList<String>
for(int i=0; i<myList.size(); i++)
{
if(myList.get(i).contains("foo"))
{
myList.remove(i);
}
}
然而,这会在我的列表中留下“空格”。我希望列表省略空元素,并在迭代后缩小到必要的大小。
有没有一种聪明的方法可以做到这一点,而无需切换到LinkedList
?
答案 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个库:Guava
和lambdaj
答案 4 :(得分:2)
ArrayList在场景后面维护一个数组。我想深入了解java.util.ArrayList
和java.util.LinkedList
。
首先,ArrayList在幕后维护一个数组。一旦创建了一个ArrayList实例,它就会创建一个大小为10的数组,并在插入元素时增长。尺寸增长到3(尺寸)/ 2 +1
这是源代码。
arrat列表的默认大小。请看constructer code。
public ArrayList() {
this(10);
}
它的大小增加到3(大小)/ 2 + 1.这里是source code。 ArrayList#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使用的内存空间。该项目将被删除,并更新其上一个和下一个项目对项目的引用。
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