Удалить данные из ArrayList с помощью цикла For-loop

У меня возникла странная проблема. Я думал, что это будет стоить мне нескольких минут, но сейчас я боюсь несколько часов... Вот что я получил:

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, но не может понять, почему.

Как это исправить? Или, может быть, другой способ их удалить?

Ответ 1

Проблема здесь в том, что вы выполняете итерацию от 0 до размера и внутри цикла вы удаляете элементы. Удаление элементов уменьшит размер списка, который не удастся при попытке получить доступ к индексам, размер которых превышает эффективный размер (размер после удаленных элементов).

Для этого есть два подхода.

Удалите с помощью итератора, если вы не хотите иметь дело с индексом.

for (Iterator<Object> it = data.iterator(); it.hasNext();) {
if (it.next().getCaption().contains("_Hardi")) {
    it.remove();
}
}

Else, удалить с конца.

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

Ответ 2

Вы не должны удалять элементы из списка, пока вы перебираете его. Вместо этого используйте Iterator.remove() как:

for (Iterator<Object> it = list.iterator(); it.hasNext();) {
    if ( condition is true ) {
        it.remove();
    }
}

Ответ 3

Каждый раз, когда вы удаляете элемент, вы меняете индекс перед ним (поэтому, когда вы удаляете список [1], список [2] становится списком [1], следовательно, пропускается.

Здесь очень простой способ: (вместо обратного отсчета)


for(int i = list.size() - 1; i>=0; i--)
{
  if(condition...)
   list.remove(i);
}

Ответ 4

Это имеет смысл, если вы обдумаете это. Скажем, у вас есть список [A, B, C]. Первый проходит через цикл, i == 0. Вы видите элемент A, а затем удалите его, поэтому теперь список [B, C], а элемент 0 - B. Теперь вы увеличиваете i в конце цикла, так что вы смотрите list[1], который равен C.

Одним из решений является уменьшение i всякий раз, когда вы удаляете элемент, чтобы он "отменял" последующий приращение. Лучшим решением, как указано выше, является использование Iterator<T>, который имеет встроенную функцию remove().

Говоря в целом, это хорошая идея, когда вы сталкиваетесь с такой проблемой, чтобы вытащить листок бумаги и притвориться, что вы компьютер - пройти каждый шаг цикла, записывая все переменные, поскольку вы идти. Это сделало бы "пропуск" ясным.

Ответ 5

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

Потому что, когда вы удаляете элемент из списка, элементы списка перемещаются вверх. Поэтому, если вы удалите первый элемент, то есть при индексе 0, элемент с индексом 1 будет смещен в индекс 0, но ваш счетчик циклов будет увеличиваться на каждой итерации. поэтому вместо того, чтобы получить обновленный элемент 0-го индекса, вы получаете 1-й элемент индекса. Поэтому просто уменьшайте счетчик каждый раз, когда вы удаляете элемент из своего списка.

Вы можете использовать приведенный ниже код, чтобы он работал нормально:

for (int i = 0; i < data.size(); i++){
    if (data.get(i).getCaption().contains("_Hardi")){
        data.remove(i);
        i--;
    }
}

Ответ 7

Уже поздно, но это может сработать для кого-то.

Iterator<YourObject> itr = yourList.iterator();

// remove the objects from list
while (itr.hasNext())
{
    YourObject object = itr.next();
    if (Your Statement) // id == 0
    {
        itr.remove();
    }
}

Ответ 8

Я не понимаю, почему это решение является лучшим для большинства людей.

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();
    }
}

Ответ 9

Это происходит потому, что, удаляя элементы, вы изменяете индекс ArrayList.

Ответ 10

Поскольку ваш индекс больше не подходит, как только вы удаляете значение

Кроме того, вы не сможете перейти на size, так как если вы удалите один элемент, размер изменится.

Для достижения этой цели вы можете использовать iterator.

Ответ 11

В дополнение к существующим ответам вы можете использовать обычный цикл while с условным приращением:

int i = 0;
while (i < data.size()) {
    if (data.get(i).getCaption().contains("_Hardi"))
        data.remove(i);
    else i++;
}

Обратите внимание, что data.size() должен вызываться каждый раз в условии цикла, в противном случае вы получите исключение IndexOutOfBoundsException, поскольку каждый удаленный элемент изменяет исходный размер вашего списка.

Ответ 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

Существует более простой способ решить эту проблему без создания нового объекта итератора. Вот концепция. Предположим, что ваш массивList содержит список имен:

names = [James, Marshall, Susie, Audrey, Matt, Carl];

Чтобы удалить все из Susie forward, просто получите индекс 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

Это обычная проблема при использовании Arraylists, и это происходит из-за того, что длина (размер) Arraylist может измениться. При удалении изменяется размер; поэтому после первой итерации ваш код переходит в режим haywire. Лучший совет - либо использовать Iterator, либо зацикливать со спины, я рекомендую цикл backword, хотя, поскольку я считаю, что он менее сложный и он по-прежнему отлично работает с многочисленными элементами:

//Let decrement!
for(int i = size-1; i >= 0; i--){
    if (data.get(i).getCaption().contains("_Hardi")){
        data.remove(i);
    }
 }

По-прежнему ваш старый код, только зацикленный по-другому!

Надеюсь, это поможет...

Веселая кодировка!!!