0

次の問題がある場合: 強化された for ループを使用しているリストがあります。リストから sth を削除するたびに、ConcurrentModificationException が発生します。この例外がスローされる理由はすでにわかっていますが、コードを変更して機能させる方法がわかりません。これは私のコードです:

for(Subject s : SerData.schedule)
    {
        //Checking of the class is already existing
        for(Classes c : s.classes)
        {
            if(c.day == day &c.which_class == which_class)
            {
                int index = getclassesindex(s.classes, new Classes(day, which_class));
                synchronized (s) {
                    s.classes.remove(index);

                }
            }
        }
            //More code....
    }

私もこの実装を試しました。

for(Subject s : SerData.schedule)
    {
        //Checking of the class is already existing
        Iterator<Classes> x = s.classes.iterator();
        while(x.hasNext())
        {
            Classes c = x.next();
            if(c.day == day &c.which_class == which_class)
            {
                int index = getclassesindex(s.classes, new Classes(day, which_class));
                synchronized (s) {
                    s.classes.remove(index);
                }
            }
        }
        //More code....
    }

どちらも機能していません...

一般的に使用される標準的なソリューションはありますか? (うまくいけば、それは明らかではありません:D)

4

7 に答える 7

3

この問題が発生する主な理由は、for-each ループのセマンティックな意味のためです。

for-each ループを使用する場合、トラバースされるデータ構造は変更できません。

基本的に、この形式のものはすべて、この例外をスローします。

for( Object o : objCollection )
{
    // ...
    if ( satisfiesSomeProperty ( o ) )
       objList.remove(o);    // This is an error!!
    // ...
}

ちなみに、コレクション内の要素を追加または置換することもできません。

この操作を実行するには、いくつかの方法があります。

1 つの方法は、反復子を使用してremove()、オブジェクトを削除するときにメソッドを呼び出すことです。

Iterator <Object> objItr = objCollection.iterator();

while(objItr.hasNext())
{
    Object o = objItr.next();
    // ...
    if ( satifiesSomeProperty ( o ) )
        objItr.remove();    // This is okay
    // ...
}

removeこのオプションには、反復子のメソッドに比例した時間でオブジェクトの削除が行われるというプロパティがあります。

次のオプションは、削除するオブジェクトを保存し、リストを走査した後にそれらを削除することです。これは、反復中の削除によって一貫性のない結果が生成される可能性がある状況で役立つ場合があります。

Collection <Object> objsToRemove = // ...
for( Object o : objCollection )
{
    // ...
    if ( satisfiesSomeProperty ( o ) )
       objsToRemove.add (o);
    // ...
}
objCollection.removeAll ( objsToRemove );

これらの 2 つの方法は一般的なCollection型で機能しますが、リストの場合は、標準の for ループを使用して、リストの末尾から先頭までリストをたどって、必要なものを削除できます。

for (int i = objList.size() - 1; i >= 0; i--)
{
    Object o = objList.get(i);
    // ...
    if ( satisfiesSomeProperty(o) )
       objList.remove(i);
    // ...
}

通常の方向に歩いて削除することもできますが、インクリメントがどのように発生するかに注意する必要があります。i具体的には、次の要素が同じインデックスにシフトダウンされるため、削除するときにインクリメントしたくありません。

for (int i = 0; i < objList.size(); i++)
{
    Object o = objList.get(i);
    // ...
    if ( satisfiesSomeProperty(o) )
    {
       objList.remove(i);
       i--;
    }

    //caveat: only works if you don't use `i` later here
    // ...
}

これが概念の良い概要を提供し、役立つことを願っています!

于 2012-06-16T11:18:08.777 に答える
1

を使用Iterator.remove()すると、例外がスローされないようにする必要があります。

于 2012-06-16T11:12:32.183 に答える
1

クラスのコレクションを繰り返し処理していて、特定のクラスがいくつかの基準に一致する場合、そのインデックスを探して削除しようとしていますか?

なぜそうしないのですか:

Iterator<Classes> x = s.classes.iterator();
while(x.hasNext()){
    Classes c = x.next();
    if(c.day == day && c.which_class == which_class) {
        x.remove();
    }
}

必要に応じて同期を追加します(ただし、私があなたの場合は並行コレクションを好みます)、できれば「==」を equals() に変更し、ゲッター/セッターなどを追加します。また、Java の慣例は、キャメルケースを使用して変数とメソッドに名前を付けることです。 (「_」で区切らないでください)。

実際、これは反復子を使用しなければならないケースの 1 つです。

于 2012-06-16T11:14:00.820 に答える
0

一般にサブクラスの一般的な解決策はありません。コレクションが変更された場合、変更がイテレーター自体を介して行われない限り、Collectionほとんどのイテレーターは無効になります。Iterator.remove()

実装に関して、潜在的な解決策があります。インターフェースには、インデックスベースの追加/取得/設定/削除操作があります。インスタンスを使用するのではなく、配列の場合と同様に、カウンターベースのループを使用してリストを明示的に反復処理できます。ただし、要素を挿入または削除するときは、ループカウンターを適切に更新するように注意する必要があります。ListListIterator

于 2012-06-16T11:10:34.553 に答える
0

ConcurrentModificationException の javadoc から:

「フェイルファスト イテレーターを使用してコレクションを反復処理しているときに、スレッドがコレクションを直接変更すると、イテレーターはこの例外をスローします。」

したがって、 for (Classes c : s.classes) 内

あなたは s.classes.remove(index) を実行しています

イテレータは、その契約が示すとおりに実行しています。ループ外のスコープでインデックスを宣言し、ループが完了したらターゲットを削除します。

于 2012-06-16T11:13:45.560 に答える
0
Iterator<Classes> classesIterator = s.classes.iterator();
while (classesIterator.hasNext()) {
    Classes c = classesIterator.next();
    if (c.day == day && c.which_class == which_class) {
        classesIterator.remove();
    }
}
于 2012-06-16T11:14:39.387 に答える
0

for-each イテレータはフェイルファストです。これが、コレクションをトラバースしながらコレクションを変更するため、削除操作が失敗する理由です。

List インターフェイスのどの実装を使用していますか? 件名の同期に気づきました。このコードを同時に使用していますか?

同時実行が必要な場合は、CopyOnWriteArrayListを使用することをお勧めします。同期は必要なく、for-each イテレーターは ConcurrentModificationException をスローしません。

于 2012-06-16T12:49:31.013 に答える