web-dev-qa-db-ja.com

Java:反復/リストへの追加中にリストから要素を削除する方法

この質問は、 この質問 で説明(および解決)された問題のより特殊なケースです。

StopAndRemove(ServerObject server)とclose()メソッドの2つのメソッドがあります。後者は、すべてのサーバーを閉じて、サーバーリストから削除する必要があります。リストは次のように定義されます

List<ServerObject> server. 

CloseAndlyOpenのstopAndRemoveとほぼ同じコードを使用したくないので、次のようなことを行います。

public void closeCurrentlyOpen() {
   for(ServerObject server : this.servers) {
       stopAndRemove(server)
   }
}

これはConcurrentModificationExceptionを引き起こすため、機能しません。リストのコピーを作ってみました

List<ServerObject> copyList = new ArrayList<ServerObject>(this.servers);

そして、それをforeachループのリストとして使用します。しかし、copyListを反復処理しているときに、他のスレッドがサーバーをサーバーリストに追加する可能性がありますが、closeCurrentlyOpenは空のリストになることになっています。 addServerToListメソッドはサーバーリストに同期されるため、これを行う

public void closeCurrentlyOpen() {
   synchronized(this.servers) {
     for(ServerObject server : this.servers) {
        stopAndRemove(server)
     }
    }
}

修正で問題を解決します。しかし、直接呼び出された場合に必要なstopAndRemoveメソッドのコードを同期できません。

この3つの方法の設計には、おそらくやり直しが必要だと思います。誰かアイデア?

17
Arvodan

メソッドstop()をstopAndRemove()から分離します。次に、明示的なイテレータを使用してループを記述し、停止してから、iterator.remove()を実行します。

メソッド名の「and」はコードのにおいです。

17
starblue

おそらくこれは間違った方法ですが、削除する必要のあるオブジェクトへのインデックスまたは参照を含む削除コレクションを常に作成します。次に、そのコレクションを反復処理し、元のコレクションからそれらのインデックス/オブジェクトを削除します。おそらく最も効率的ではありませんが、それで仕事は終わりました。

の代わりに

for(Collection things : thing)  
    things.remove(thing)

私が使う

Collection toRemove = new LinkedList();
for(things : thing)
    toRemove.add(thing);

for(toRemove : thing)
    things.remove(thing)
14
Bob Gettys

これを以前に行ったときは、常に「古い学校」のLinkedListコレクション、Iterator、およびIterator.remove()メソッドを使用して、現在のアイテムを削除しました。

4
Eric Petroelje

ConcurrentModificationExceptionに関するこの記事 がこの分野でいくつかのアドバイスを見つけるかもしれません。

3
Alex Miller

すべてのServerObject停止コードをstopAndRemoveからプライベートstopServerメソッドにリファクタリングし、stopAndRemoveおよびcloseCurrentlyOpenで個別に削除を実行します。次に、ListIteratorを使用してそれらを削除できます(またはforループでそれらをすべて停止し、最後にリストをクリアします)。

2
Pesto

イテレータを取得し、それを使用して削除する必要があります。イテレータはJavaでは fail-fast であるため、例外が発生します。

1
amit

与えられた例の具体的な詳細ではなく、質問のタイトルに答える。実際、このソリューションは特定の状況では適切ではありません(他の人が示唆するように、リファクタリングは適切です)。

ただし、多くのJavaプログラマは CopyOnWriteArrayList (1.5以降のJDKの一部)を認識しておらず、同じ問題に対する独自の解決策(ロール反復前のリスト)。

1
Neeme Praks

... XML以外のファイルをディレクトリリストから削除しています...

List<File> files = Arrays.asList(dir.listFiles());

Iterator<File> i = files.iterator();

while (i.hasNext()) {
    File file = i.next();
    if (!file.getName().endsWith(".xml")) {
        i.remove();
    }
}
1
Spider

Firebird84に似ています。しかし、あなたはremoveAll(Collection c)apiを使用できます

for(String exitingPermission : existingPermissions){                
    //remove all permissions for the screen and add the new ones
    if(exitingPermission.split("_")[0].equals(screen)){
        removePermissions.add(exitingPermission);
    }
 }
existingPermissions.removeAll(removePermissions);
1
Raskolnikov