反復処理中にコレクションから要素を削除する 質問する

反復処理中にコレクションから要素を削除する 質問する

私の知る限り、2 つのアプローチがあります。

  1. コレクションのコピーを反復処理する
  2. 実際のコレクションのイテレータを使用する

例えば、

List<Foo> fooListCopy = new ArrayList<Foo>(fooList);
for(Foo foo : fooListCopy){
    // modify actual fooList
}

そして

Iterator<Foo> itr = fooList.iterator();
while(itr.hasNext()){
    // modify actual fooList using itr.remove()
}

一方のアプローチを他方のアプローチよりも優先する理由はありますか (たとえば、単純に読みやすさという理由で最初のアプローチを優先するなど)?

ベストアンサー1

を回避するための代替案をいくつか挙げてみますConcurrentModificationException

次のような本のコレクションがあるとします

List<Book> books = new ArrayList<Book>();
books.add(new Book(new ISBN("0-201-63361-2")));
books.add(new Book(new ISBN("0-201-63361-3")));
books.add(new Book(new ISBN("0-201-63361-4")));

収集と削除

最初の手法は、削除するすべてのオブジェクトを収集し (たとえば、拡張 for ループを使用)、反復処理が完了したら、見つかったすべてのオブジェクトを削除することです。

ISBN isbn = new ISBN("0-201-63361-2");
List<Book> found = new ArrayList<Book>();
for(Book book : books){
    if(book.getIsbn().equals(isbn)){
        found.add(book);
    }
}
books.removeAll(found);

これは、実行したい操作が「削除」であると仮定しています。

「追加」したい場合もこのアプローチは機能しますが、別のコレクションを反復処理して、2 番目のコレクションに追加する要素を決定し、addAll最後にメソッドを発行することを想定しています。

ListIterator の使用

ListIteratorリストを操作している場合、反復処理中に項目の削除と追加をサポートする を使用するという別の手法もあります。

ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
    if(iter.next().getIsbn().equals(isbn)){
        iter.remove();
    }
}

繰り返しになりますが、上記の例では「remove」メソッドを使用しましたが、これはご質問の意図するところのようですが、add反復中に新しい要素を追加するためにそのメソッドを使用することもできます。

JDK >= 8 の使用

Java 8 またはそれ以上のバージョンを使用している場合は、それを活用するために使用できる他のテクニックがいくつかあります。

removeIf基本クラスで新しいメソッドを使用できますCollection

ISBN other = new ISBN("0-201-63361-2");
books.removeIf(b -> b.getIsbn().equals(other));

または、新しいストリーム API を使用します。

ISBN other = new ISBN("0-201-63361-2");
List<Book> filtered = books.stream()
                           .filter(b -> b.getIsbn().equals(other))
                           .collect(Collectors.toList());

この最後のケースでは、コレクションから要素をフィルター処理するには、元の参照をフィルター処理されたコレクション (つまり ) に再割り当てするか、フィルター処理されたコレクションを元のコレクション (つまり ) から見つかった要素books = filteredに使用します。removeAllbooks.removeAll(filtered)

サブリストまたはサブセットを使用する

他にも方法があります。リストがソートされていて、連続する要素を削除したい場合は、サブリストを作成してからクリアすることができます。

books.subList(0,5).clear();

サブリストは元のリストに基づいているため、この要素のサブコレクションを削除するにはこれが効率的な方法となります。

NavigableSet.subSet同様のことは、メソッド、またはそこで提供されているスライス メソッドのいずれかを使用してソート セットで実現できます。

考慮事項:

どのような方法を使用するかは、何をしようとしているかによって異なります。

  • collect andremoveAlテクニックは、任意のコレクション (コレクション、リスト、セットなど) で機能します。
  • この手法は、リストの実装が追加および削除操作をサポートしているListIterator場合にのみ機能します。ListIterator
  • このIteratorアプローチはどのタイプのコレクションでも機能しますが、削除操作のみをサポートします。
  • ListIterator/アプローチIteratorの明らかな利点は、反復処理時に削除するため、何もコピーする必要がないことです。したがって、これは非常に効率的です。
  • JDK 8 ストリームの例では、実際には何も削除せず、必要な要素を検索し、元のコレクション参照を新しいものに置き換え、古いものをガベージ コレクションしました。そのため、コレクションを 1 回だけ反復処理すれば効率的です。
  • collect andremoveAllアプローチの欠点は、2 回反復する必要があることです。最初に、削除基準に一致するオブジェクトを探して foo-loop で反復し、それを見つけたら、元のコレクションから削除するように要求します。これは、削除するためにこのアイテムを探す 2 回目の反復作業を意味します。
  • インターフェースの削除メソッドはIteratorJavadoc で「オプション」としてマークされているので、削除メソッドを呼び出すとIterator例外をスローする実装が存在する可能性があることに言及する価値があるUnsupportedOperationExceptionと思います。したがって、要素の削除に対するイテレータのサポートを保証できない場合、このアプローチは他のアプローチよりも安全性が低いと言えます。

おすすめ記事