web-dev-qa-db-ja.com

LinkedList:オブジェクトを削除します

これは、Java for eachループを使用してLinkedListからアイテムを見つけて削除する有効な方法ですか?不整合が発生する可能性があります:

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

他の人は、通常これはコレクションからオブジェクトをremoveする方法ではないという有効な点について言及しています。ただし、この場合は、breakを実行すると、ループからremoveが外れるため、問題ありません。

ただし、removeの後で繰り返しを続けたい場合は、イテレーターを使用する必要があります。そうしないと、ConcurrentModificationException、またはより一般的なケースでは未定義の動作が発生します。

そうです、breakの後にforeachからremoveを外すと、大丈夫です


foreachのコレクションを変更できないため、これは失敗すると言っている人にとっては、これは繰り返しを続けたい場合にのみ当てはまります。ここではそうではないので、このショートカットは問題ありません。

ConcurrentModificationExceptionは、イテレータによってチェックされ、スローされます。ここで、remove(同時変更と見なされます)の後、breakはループから外れます。イテレータはそれを検出する機会すらありません。

breakにコメントを追加するのが最善かもしれません、なぜそれが絶対に必要なのかなど。このコードが後でremoveの後に反復を続けるように変更されると、失敗します

私はこのイディオムをgoto(またはむしろbreak/continueとラベル付け)と同様に扱います。最初は間違っているように見えるかもしれませんが、賢明に使用すると、よりクリーンなコードになります。

17

オブジェクトを削除するためにコレクションを反復処理してオブジェクトを検索する場合は、イテレーターを使用し、そのremoveメソッドを使用することをお勧めします。それの訳は

  1. コレクションは、たとえば、リンクリスト(およびあなたの場合はそうです)であり、そのremoveメソッドはオブジェクトをもう一度検索することを意味します。この検索はO(n)複雑になる可能性があります。
  2. イテレータのremoveメソッドを使用しない限り、削除後に反復を続行することはできません。現在、最初のオカレンスを削除しています。将来、一致するオカレンスをすべて削除する必要がある場合があります。その場合は、ループを書き直す必要があります。

原則として、次のようなものを拡張して使用することをお勧めします。

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

そうすれば、将来変更される可能性のある基になるリストについての仮定を立てることはありません。


コードを比較して、イテレータremove(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;
}
7
Lawrence Dol

iterator.remove() を使用する必要があります:

イテレータによって返された最後の要素を基になるコレクションから削除します(オプションの操作)。このメソッドは、nextへの呼び出しごとに1回だけ呼び出すことができます。このメソッドを呼び出す以外の方法で反復の進行中に基になるコレクションが変更された場合、イテレーターの動作はunspecifiedです。

6
JRL

編集:確かに、それは休憩のおかげで失敗することはありません。詳細については、polygenelubricantの回答を参照してください。

ただし、これは危険な方法です。 Javaでコレクションの反復と変更を同時に行うには、「ListIterator」オブジェクトを使用し、イテレータ独自の「add()」メソッドと「remove()」メソッドを使用する必要があります。コレクションのメソッドは使用しないでください。

「Java.util.Iterator」クラスと「Java.util.ListIterator」クラスのJavaドキュメントを確認できます。

4
Zorglub

次のようなものを試してください。

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

これは、イテレータをforeachループで置き換えることができない最後の場所の1つです。

1
Stroboskop

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で機能し、2番目の提案はリストを必要とします。

1
whiskeysierra

上記の2番目のループは少し変更する必要があります

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);
    }
}
0
fatbone

CopyOnWriteArrayList はあなたが探しているものかもしれません。変異演算が実行されると、基になる配列のコピーが作成されます。これにより、for-eachループ内でリスト要素を変更できます。ただし、これはリンクリストではなく、非常に非効率的である可能性があることに注意してください。

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

}
0
William Brendel