これは、Java for eachループを使用してLinkedListからアイテムを見つけて削除する有効な方法ですか?不整合が発生する可能性があります:
for(ObjectType ob : obList) {
if(ob.getId() == id) {
obList.remove(ob);
break;
}
}
他の人は、通常これはコレクションからオブジェクトをremove
する方法ではないという有効な点について言及しています。ただし、この場合は、break
を実行すると、ループからremove
が外れるため、問題ありません。
ただし、remove
の後で繰り返しを続けたい場合は、イテレーターを使用する必要があります。そうしないと、ConcurrentModificationException
、またはより一般的なケースでは未定義の動作が発生します。
そうです、break
の後にforeach
からremove
を外すと、大丈夫です。
foreach
のコレクションを変更できないため、これは失敗すると言っている人にとっては、これは繰り返しを続けたい場合にのみ当てはまります。ここではそうではないので、このショートカットは問題ありません。
ConcurrentModificationException
は、イテレータによってチェックされ、スローされます。ここで、remove
(同時変更と見なされます)の後、break
はループから外れます。イテレータはそれを検出する機会すらありません。
break
にコメントを追加するのが最善かもしれません、なぜそれが絶対に必要なのかなど。このコードが後でremove
の後に反復を続けるように変更されると、失敗します。
私はこのイディオムをgoto
(またはむしろbreak
/continue
とラベル付け)と同様に扱います。最初は間違っているように見えるかもしれませんが、賢明に使用すると、よりクリーンなコードになります。
オブジェクトを削除するためにコレクションを反復処理してオブジェクトを検索する場合は、イテレーターを使用し、その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;
}
iterator.remove()
を使用する必要があります:
イテレータによって返された最後の要素を基になるコレクションから削除します(オプションの操作)。このメソッドは、nextへの呼び出しごとに1回だけ呼び出すことができます。このメソッドを呼び出す以外の方法で反復の進行中に基になるコレクションが変更された場合、イテレーターの動作はunspecifiedです。
編集:確かに、それは休憩のおかげで失敗することはありません。詳細については、polygenelubricantの回答を参照してください。
ただし、これは危険な方法です。 Javaでコレクションの反復と変更を同時に行うには、「ListIterator」オブジェクトを使用し、イテレータ独自の「add()」メソッドと「remove()」メソッドを使用する必要があります。コレクションのメソッドは使用しないでください。
「Java.util.Iterator」クラスと「Java.util.ListIterator」クラスのJavaドキュメントを確認できます。
次のようなものを試してください。
Iterator<ObjectType> iter = obList.iterator();
while (iter.hasNext()) {
ObjectType ob = iter.next();
if(ob.getId() == id) {
iter.remove();
break;
}
}
これは、イテレータをforeachループで置き換えることができない最後の場所の1つです。
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番目の提案はリストを必要とします。
上記の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);
}
}
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);
}
}