繰り返したいArrayListがあります。それを繰り返しながら、私は同時に要素を削除する必要があります。明らかにこれはJava.util.ConcurrentModificationException
を投げます。
この問題に対処するためのベストプラクティスは何ですか?最初にリストを複製する必要がありますか?
ループ自体ではなく、コードの別の部分から要素を削除します。
私のコードはこのようになります:
public class Test() {
private ArrayList<A> abc = new ArrayList<A>();
public void doStuff() {
for (A a : abc)
a.doSomething();
}
public void removeA(A a) {
abc.remove(a);
}
}
a.doSomething
はTest.removeA()
を呼び出すことがあります。
2つの選択肢
originalList.removeAll(valuesToRemove)
を呼び出しますremove()
メソッドを使用してください。これは拡張forループを使用できないことを意味します。2番目のオプションの例として、リストから5を超える長さの文字列を削除します。
List<String> list = new ArrayList<String>();
...
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
String value = iterator.next();
if (value.length() > 5) {
iterator.remove();
}
}
ArrayListのJavaDocsから
このクラスのiteratorメソッドとlistIteratorメソッドによって返される反復子は非常に高速です。反復子が作成された後でリストが構造的に変更されている場合は、反復子自身のremoveまたはadd以外の方法で methodsを指定すると、イテレータはConcurrentModificationExceptionをスローします。
あなたは高度な "for loop"でリストから値を削除しようとしていますが、たとえトリックを適用したとしても不可能です。よりよい方法は、ここで推奨されているようにイテレータレベルをコーディングすることです。
人々がどのように伝統的なforループアプローチを提案していないのだろうか。
for( int i = 0; i < lStringList.size(); i++ )
{
String lValue = lStringList.get( i );
if(lValue.equals("_Not_Required"))
{
lStringList.remove(lValue);
i--;
}
}
これもうまくいきます。
あなたは本当に伝統的な方法で単に配列を反復するべきです
リストから要素を削除するたびに、それ以降の要素はプッシュフォワードになります。反復要素以外の要素を変更しない限り、次のコードが機能します。
public class Test(){
private ArrayList<A> abc = new ArrayList<A>();
public void doStuff(){
for(int i = (abc.size() - 1); i >= 0; i--)
abc.get(i).doSomething();
}
public void removeA(A a){
abc.remove(a);
}
}
Java 8では、Collection Interfaceを使用し、removeIfメソッドを呼び出すことによってこれを実行できます。
yourList.removeIf((A a) -> a.value == 2);
より多くの情報が見つかります ここ
通常の方法でループを実行してください。Java.util.ConcurrentModificationException
は、アクセスされる要素に関連するエラーです。
だから試してみてください。
for(int i = 0; i < list.size(); i++){
lista.get(i).action();
}
これは、削除するオブジェクトを追加するために別のリストを使用し、その後、元のリストから要素を削除するためにstream.foreachを使用する例です。
private ObservableList<CustomerTableEntry> customersTableViewItems = FXCollections.observableArrayList();
...
private void removeOutdatedRowsElementsFromCustomerView()
{
ObjectProperty<TimeStamp> currentTimestamp = new SimpleObjectProperty<>(TimeStamp.getCurrentTime());
long diff;
long diffSeconds;
List<Object> objectsToRemove = new ArrayList<>();
for(CustomerTableEntry item: customersTableViewItems) {
diff = currentTimestamp.getValue().getTime() - item.timestamp.getValue().getTime();
diffSeconds = diff / 1000 % 60;
if(diffSeconds > 10) {
// Element has been idle for too long, meaning no communication, hence remove it
System.out.printf("- Idle element [%s] - will be removed\n", item.getUserName());
objectsToRemove.add(item);
}
}
objectsToRemove.stream().forEach(o -> customersTableViewItems.remove(o));
}
一つのオプションはこれにremoveA
メソッドを変更することです -
public void removeA(A a,Iterator<A> iterator) {
iterator.remove(a);
}
しかしこれは、あなたのdoSomething()
がiterator
をremove
メソッドに渡すことができるはずであることを意味します。あまり良い考えではありません。
これを2段階の方法で行うことができます。選択した要素を削除するのではなく、リストを反復処理する最初のループで、markそれらを削除対象とします。そのためには、これらの要素を別のList
に単純にコピー(シャローコピー)することができます。
その後、繰り返しが終わったら、最初のリストからremoveAll
を、2番目のリストのすべての要素を単純に実行します。
リストを繰り返しながら、要素を削除したい場合は可能です。以下に私の例を見てみましょう。
ArrayList<String> names = new ArrayList<String>();
names.add("abc");
names.add("def");
names.add("ghi");
names.add("xyz");
上記の配列リストの名前があります。そして私は上記のリストから "def"の名前を削除したいです。
for(String name : names){
if(name.equals("def")){
names.remove("def");
}
}
上記のコードでは、反復中にリストを変更しているため、ConcurrentModificationException例外がスローされます。
このようにして、Arraylistから "def"という名前を削除するには、
Iterator<String> itr = names.iterator();
while(itr.hasNext()){
String name = itr.next();
if(name.equals("def")){
itr.remove();
}
}
上記のコードでは、反復子を使用してArraylistから "def"という名前を削除し、配列を印刷しようとすると、次のような出力が表示されます。
出力:[abc、ghi、xyz]
For each loopの代わりに、normal for loopを使用してください。たとえば、次のコードでは、Java.util.ConcurrentModificationExceptionを返さずに、配列リスト内のすべての要素を削除します。ユースケースに従ってループ内の条件を変更できます。
for(int i=0;i<abc.size();i++) {
e.remove(i);
}
このような簡単なことをしてください。
for (Object object: (ArrayList<String>) list.clone()) {
list.remove(object);
}
ストリームを使用した代替Java 8ソリューション
theList = theList.stream()
.filter(element -> !shouldBeRemoved(element))
.collect(Collectors.toList());
Java 7では、代わりにGuavaを使用できます。
theList = FluentIterable.from(theList)
.filter(new Predicate<String>() {
@Override
public boolean apply(String element) {
return !shouldBeRemoved(element);
}
})
.toImmutableList();
Guavaの例では不変のリストが表示されるので注意してください。
ArrayListの代わりにCopyOnWriteArrayListを使用することもできます。これはJDK 1.5以降で推奨される最新のアプローチです。
あなたの目標がリストからすべての要素を削除することであるなら、あなたはそれぞれのアイテムを反復処理してから呼び出すことができます:
list.clear()
配列リストの代わりにイテレータを使う
型が一致するセットをイテレータに変換する
そして次の要素に移動して削除する
Iterator<Insured> itr = insuredSet.iterator();
while (itr.hasNext()) {
itr.next();
itr.remove();
}
次の要素に移動することは、要素を削除するためのインデックスが必要なので、ここで重要です。
遅刻して私は知っていますが、私はこの解決策がシンプルでエレガントだと思うのでこれに答えます。
List<String> listFixed = new ArrayList<String>();
List<String> dynamicList = new ArrayList<String>();
public void fillingList() {
listFixed.add("Andrea");
listFixed.add("Susana");
listFixed.add("Oscar");
listFixed.add("Valeria");
listFixed.add("Kathy");
listFixed.add("Laura");
listFixed.add("Ana");
listFixed.add("Becker");
listFixed.add("Abraham");
dynamicList.addAll(listFixed);
}
public void updatingListFixed() {
for (String newList : dynamicList) {
if (!listFixed.contains(newList)) {
listFixed.add(newList);
}
}
//this is for add elements if you want eraser also
String removeRegister="";
for (String fixedList : listFixed) {
if (!dynamicList.contains(fixedList)) {
removeResgister = fixedList;
}
}
fixedList.remove(removeRegister);
}
これは一つのリストから他のリストへ更新するためのものであり、あなたは一つのリストからすべてを作ることができ、メソッドの更新であなたは両方のリストをチェックしてリスト間の要素を消すか追加することができます。これは両方のリストが常に同じサイズであることを意味します
「最初にリストを複製する必要がありますか?」
それが最も簡単な解決策になり、クローンから削除し、削除後にクローンをコピーします。
私のrummikubゲームからの例:
SuppressWarnings("unchecked")
public void removeStones() {
ArrayList<Stone> clone = (ArrayList<Stone>) stones.clone();
// remove the stones moved to the table
for (Stone stone : stones) {
if (stone.isOnTable()) {
clone.remove(stone);
}
}
stones = (ArrayList<Stone>) clone.clone();
sortStones();
}