Удаление элементов из ArrayList
Я пытаюсь удалить определенные элементы из ArrayList<String>
for(int i=0; i<myList.size(); i++)
{
if(myList.get(i).contains("foo"))
{
myList.remove(i);
}
}
Однако это оставляет "пустые места" в моем списке. Я хотел бы, чтобы в списке оставались пустые элементы и после итерации через него сжимались до необходимого размера.
Есть ли разумный способ сделать это, не переключаясь на LinkedList
?
Ответы
Ответ 1
Однако это оставляет "пустые места" в моем списке.
Нет, это не так. Он полностью удаляет записи из списка. Другие элементы перемещаются соответствующим образом. То, что он делает с тем, как вы написали, пропускает проверку для следующей записи... потому что это будет "перетасовано вниз", чтобы быть элементом i
, но вы затем посмотрите на элемент i + 1
.
Один простой способ избежать этого - вместо этого работать назад:
for (int i = myList.size() - 1; i >= 0; i--) {
if (myList.get(i).contains("foo")) {
myList.remove(i);
}
}
Или используйте итератор, как указано в других ответах, конечно. Оба будут работать - приведенный выше код может быть немного более эффективным, если вы удаляете несколько записей, тем не менее, поскольку к тому времени, когда вы дойдете до начала, будет меньше изменений. Маловероятно, что это будет значительным.
Несчастливо, что для использования решения итератора вам нужно явно использовать итератор - вы не можете удалить из коллекции, используя усиленный для цикла.
Ответ 2
Используйте Iterator
и вызовите Iterator.remove()
.
Iterator it = myList.iterator();
while(it.hasNext()) {
if (it.next().contains("foo")) {
it.remove();
}
}
Таким образом вы также избегаете проблем для уменьшения размера списка, полагаясь на него как условие выхода на ваш цикл, и обращаетесь к нему с использованием индексов, которые могут быть разными.
Конечно, итерация через список назад также будет работать.
Ответ 3
Разумным способом, который вы ищете, является интерфейс Iterator
. Например:
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String nextItem = it.next();
if (nextItem.contains("foo")) {
it.remove();
}
}
Ответ 4
Под влиянием Scala
и функционального программирования я бы рекомендовал вам просто скопировать ваши значения в новый список для неизменяемости.
List<String> filtered = new ArrayList<String>();
for (String s : myList) {
if (!s.contains("foo")) {
filtered.add(s);
}
}
Я бы также рекомендовал 2 библиотеки для тестирования: Guava
и lambdaj
Ответ 5
ArrayList поддерживает массив за сценой. Я хочу глубоко в исходный код java.util.ArrayList
и java.util.LinkedList
.
Прежде всего, ArrayList поддерживает массив за кулисами. Когда вы создаете экземпляр ArrayList, он создает массив размером 10 и растет, пока вставлены элементы. Размер увеличивается до 3 (размер)/2 +1
Вот исходный код.
Размер по умолчанию для списка адресов. Посмотрите код конструктора.
public ArrayList() {
this(10);
}
его размер увеличивается до 3 (размер)/2 + 1. Вот исходный код . ArrayList # securityCapacity метод называется 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. Он удаляется из списка, а другие элементы списка перемещаются вниз до места удаленных объектов. Обратите особое внимание, ссылка на этот объект установлена равной нулю, и объект становится доступным для GC, но для ArrayList все еще имеется ссылка. Размер массива за ArrayList одинаковый.
Вот исходный код
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;
}
Как ответил Джон Скит, когда элемент удаляется, следующий элемент для удаляемого элемента будет находиться в удаленном месте.
Однако выделенное пространство памяти остается неизменным после удаления. java.util.LinkedList - эта проблема. Все элементы внутри LinkedList динамически распределяются и освобождаются (это, конечно же, работа GC)
java.util.LinkedList поддерживает дважды связанный список за кулисами. Каждая операция добавления и удаления изменяет пространство памяти, используемое 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.
Ответ 6
После удаления списка автоматически будет уменьшаться.
Предположим, что вы удаляете элемент в индексе 3, этот элемент будет удален, список будет уменьшаться, а элемент, который был в индексе 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--;
}
}
Ответ 7
Нет, он не оставляет "пустых мест" в вашем списке, но вы пропустите удаление всех необходимых элементов из вашего списка.
Давайте попробуем пояснить ниже.
У меня есть ArrayList с 4 элементами. (А, б, в, г).
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