12

以下の2つのJavaクラスがあります

import java.util.*;

public class ArrayListTest032 {
    public static void main(String[] ar) {
        List<String> list = new ArrayList<String>();
        list.add("core java");
        list.add("php");
        list.add("j2ee");
        list.add("struts");
        list.add("hibernate");

        Iterator<String> itr = list.iterator();

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }
        list.remove("php");

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }

    }
}

上記のコードを実行すると、以下の出力が得られます。

core java
php
j2ee
struts
hibernate

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
    at java.util.AbstractList$Itr.next(AbstractList.java:343)
    at ArrayListTest032.main(ArrayListTest032.java:20)

繰り返しながらリストを変更しているため、これは予想されます。ただし、以下の Java クラスでは、同じロジックがセット ファミリによって実行されます。

import java.util.*;

public class HashSetTest021 {
    public static void main(String[] ar) {
        Set<String> set = new HashSet<String>();
        set.add("core java");
        set.add("php");
        set.add("j2ee");
        set.add("struts");
        set.add("hibernate");

        Iterator<String> itr = set.iterator();

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }
        set.remove("php");

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }

    }
}

そしてアウトプットです。

hibernate
core java
j2ee
php
struts

ConcurrentModificationExceptionはありません。

家族の場合に同じコードが ConcurrentModificationExceptionをスローする理由を知りたいのですが、家族の場合にConcurrentModificationExceptionlistはありませんset

4

6 に答える 6

5

これは一種の「レトログラード」動作です。イテレータは、完全にトラバースすると再利用できなくhasNextなります。つまり、リストの最後に到達すると、メソッドは false を返す必要があります。

ただし、この場合、によって返されるイテレータはArrayList.iterator内部実装クラスであり、コードは次のhasNextとおりです。

public boolean hasNext() {
    return cursor != size;
}

したがってhasNext、2 番目のループを呼び出すと、最初の反復後にリストのサイズを変更する操作を実行したため、反復する項目がさらにあることが (誤って) 示されます。意味的には、リストの最後に到達した後、リスト内のアイテムを反復処理し続けることはできませんが、この実装の詳細により、2 番目の while ループに進むことができます。もちろん、その時点で、バッキング リストで行った変更により、同時変更の例外が発生します。

一方、ハッシュ セットで使用される反復子は、hasNext次のように実装されています。

public final boolean hasNext() {
    return next != null;
}

この実装は、反復が完了した後にハッシュ セットに加えられた変更に対してたまたま「脆弱」ではないため、hasNextメソッドはより適切に動作します。

于 2013-02-26T14:49:40.267 に答える
4

これは実装上の違いです。配列リストによって返される反復子は、長さをチェックするため、最後に配置されている場合でも同時変更を検出します。一方、 、 、 の反復子は、同時変更をチェックする前に最後に配置されているかどうかをチェックするため、この状態を検出しませHashSetん。ドキュメントでは、反復子が同時変更をスローしないように許可しているため、両方のアプローチが有効です。TreeSetLinkedList

于 2013-02-26T14:52:32.470 に答える
2

まず、IteratorのJavaDocを読んでください。ConcurrentModificationExceptionそれはどこかに言及していますか?

ここで、 ConcurrentModificationExceptionの JavaDoc を読み、次の点に注意してください (強調を追加)。

この例外、そのような変更が許可されていない場合に、オブジェクトの同時変更を検出したメソッドによってスローされる場合があります。

コードをよく見てください。ループはコレクションのすべての要素を反復処理します (最初の例の出力はこれを示していませwhileんが、出力を編集したか、これが実際のコードではないことがわかります)。要素を削除すると、反復する項目がなくなるため、2 番目のループは常にすぐに終了する必要があります。

したがって、リスト反復子の実装者は、反復する要素がなくなった場合でもその例外をスローすることを選択しましたが、セット反復子の実装者はそうしないことを選択しました。仕様を考えると、どちらの場合も完全に許容されます。

于 2013-02-26T14:51:17.593 に答える
1

イテレータ以外でセットに対して何かを行うと、Hashset は ConcurrentModificationException をスローする可能性があります。ただし、可能であれば反復を完了することを目標に、反復子の高速失敗動作に関する多くのヒューリスティックがあります。JavaDocs は、その動作についてかなり明確に見えます。

于 2013-02-26T14:57:26.160 に答える
1
 public static void main(String[] ar) {
            List<String> list = new ArrayList<String>();
            list.add("core java");
            list.add("php");
            list.add("j2ee");
            list.add("struts");
            list.add("hibernate");

            Iterator<String> itr = list.iterator();

            while (itr.hasNext()) {
                System.out.println(itr.next());
            }
            list.remove("php");

          /*  while (itr.hasNext()) {
                System.out.println(itr.next());
            }*/

        }

problem in itr object.it holds the list object reference
于 2013-02-26T14:49:24.557 に答える
0

リストの場合、最初のループ Iterator itr = set.iterator(); でトラバースします。

    while (itr.hasNext()) {
        System.out.println(itr.next());
    }

カーソルの値とサイズは同じになります。カーソルには、トラバースされた要素の総数の値が含まれており、リスト トラバーサルの hashNext() メソッド内には次のようなコードが含まれています。

  public boolean hasNext() {
            return cursor != size;
        }

したがって、最初の while ループ カーソル == サイズの後。ただし、リストから要素を削除した後、サイズは (originalSize-1) になります。したがって、次の while ループでは、while 内と itr.next() メソッド内に入り、modcount の変更をチェックし、ConcurrentModificationException をスローします。

Set の場合、すべての itr.hasnext() 呼び出しに対して next != null をチェックします。そして、ループを最初にトラバースした後、次は null になります。セットから要素を削除しても、次の値は null として影響されず、itr.hasNext は next == を返します。 null が true であるため、while ループに入って modcount の変更をチェックしません。したがって、ConcurrentModification Exception はスローされません。

于 2017-12-11T08:03:12.183 に答える