LinkedList: удалить объект

Является ли это допустимым способом поиска и удаления элемента из LinkedList в Java с использованием для каждого цикла, возможно ли возникновение несоответствия:

for(ObjectType ob : obList) {
  if(ob.getId() == id) {
    obList.remove(ob);
    break;
   }
}

Ответы

Ответ 1

Другие упомянули действительную точку, что обычно это не то, как вы remove объект из коллекции. ОДНАКО, в этом случае это прекрасно, так как вы break вышли из цикла после того, как вы remove.

Если вы хотите продолжить повторение после remove, вам нужно использовать итератор. В противном случае вы получите ConcurrentModificationException или в более общем случае, поведение undefined.

Итак, да, , если вы break вышли из foreach после remove, вы будете в порядке.


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

A ConcurrentModificationException проверяется и выдается итератором. Здесь, после remove (который квалифицируется как параллельная модификация), вы break выходите из цикла. Итератор даже не имеет возможности его обнаружить.

Может быть, лучше добавить комментарий к break, почему это абсолютно необходимо и т.д., потому что , если этот код впоследствии будет изменен для продолжения итерации после remove, он будет терпеть неудачу.

Я бы рассматривал эту идиому, похожую на goto (точнее, помеченную как break/continue): сначала это может показаться неправильным, но при разумном использовании это делает более чистый код.

Ответ 2

Лучше всего использовать итератор и использовать метод удаления при поиске объекта путем итерации по коллекции, чтобы удалить его. Это потому, что

  • Коллекция может быть, например, связанным списком (и в вашем случае это), метод удаления которого означает поиск объекта снова и снова, и этот поиск может иметь сложность O (n).
  • Вы не можете продолжить итерацию после удаления, если не используете метод удаления итератора. Сейчас вы удаляете первое вхождение - в будущем вам может понадобиться удалить все совпадающие вхождения, и тогда вам придется переписать цикл.

Я рекомендую, в принципе, отказаться от улучшенного и использовать что-то вроде этого:

for(Iterator<ObjectType> it=obList.iterator(); it.hasNext(); ) {
    if(it.next().getId()==id) { 
        it.remove(); 
        break;
        }
    } 

Таким образом, вы не делаете предположений о базовом списке, который может измениться в будущем.


Сравните код для удаления последней записи, вызванной удалением итератора (форматирование Sun):

private E remove(Entry<E> e) {
    if (e == header)
        throw new NoSuchElementException();

    E result = e.element;
    e.previous.next = e.next;
    e.next.previous = e.previous;
    e.next = e.previous = null;
    e.element = null;
    size--;
    modCount++;
    return result;
}

против того, что должен сделать remove (Object):

public boolean remove(Object o) {
    if (o==null) {
        for (Entry<E> e = header.next; e != header; e = e.next) {
            if (e.element==null) {
                remove(e);
                return true;
            }
        }
    } else {
        for (Entry<E> e = header.next; e != header; e = e.next) {
            if (o.equals(e.element)) {
                remove(e);
                return true;
            }
        }
    }
    return false;
}

Ответ 3

Вы должны использовать iterator.remove():

Удаляет из базовой коллекции последний элемент, возвращаемый итератор (дополнительная операция). Эта метод можно вызвать только один раз за звоните в следующий. Поведение итератор не указан, если базовая коллекция модифицирована пока итерация продолжается любым способом, кроме как путем вызова этого Метод.

Ответ 4

Edit: В самом деле, это не провалится из-за перерыва. Подробнее см. В описании полигенного смазочного материала.

Однако это опасный способ сделать. Чтобы одновременно выполнять итерацию и изменение коллекции на Java, вы должны использовать объект "ListIterator" и использовать собственные методы "add()" и "remove()" итератора, а не использовать те, которые содержатся в коллекции.

Вы можете проверить java-документ для классов "java.util.Iterator" и "java.util.ListIterator"

Ответ 5

Попробуйте что-то вроде этого:

Iterator<ObjectType> iter = obList.iterator();
while (iter.hasNext()) {
  ObjectType ob = iter.next();
  if(ob.getId() == id) {
    iter.remove();
    break;
  }
}

Это одно из последних мест, где Iterator не может быть заменен циклом foreach.

Ответ 6

Чтобы избежать ConcurrentModifiationException, вы можете сделать:

final Iterator<ObjectType> i = obList.iterator();
while (i.hasNext()) {
    if (i.next().getId() == id) {
        i.remove();
    }
}

или

for (int i = 0; i < obList.size(); i++) {
    if (obList[i].getId() == id) {
        obList.remove(i);
    }
}

Я бы предпочел первый. Обработка индексов более ошибочна, и итератор может быть эффективно реализован. И первое предложение работает с Iterable, а второе требует List.

Ответ 7

A CopyOnWriteArrayList может быть тем, что вы ищете. Когда выполняются мутационные операции, создается копия базового массива. Это позволяет изменять элементы списка внутри внутри каждого цикла. Помните, что это не связанный список и может быть довольно неэффективным.

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class Main {

    public static void main(String[] args) {
        List<String> myList = new CopyOnWriteArrayList<String>();

        myList.add("a");
        myList.add("b");
        myList.add("c");

        // Will print [a, b, c]
        System.out.println(myList);

        for (String element : myList) {
            if (element.equals("a")) {
                myList.remove(element);
            }
        }

        // Will print [b, c]
        System.out.println(myList);
    }

}

Ответ 8

Вышеупомянутый второй цикл должен быть немного изменен

for (int i = 0; i < obList.size(); ) {
    if (obList.get(i).getId() == id) {
        obList.remove(i);
        continue
    }
    ++i;
}

или

for (int i = obList.size() - 1; i >= 0; --i) {
    if (obList.get(i).getId() == id) {
        obList.remove(i);
    }
}