状況:カスタムオブジェクトのTreeSetがあり、カスタムコンパレータも使用しました。このTreeSetで使用するイテレータを作成しました。
TreeSet<Custom> ts=new TreeSet<Custom>();
Iterator<Custom> itr=ts.iterator();
while(itr.hasNext()){
Custom c=itr.next();
//Code to add a new element to the TreeSet ts
}
QUESTION: whileループ内でTreeSetに新しい要素を追加すると、その新しい要素はすぐに並べ替えられることを知りたいです。言い換えると、whileループ内に新しい要素を追加し、それが現在cで保持している要素よりも小さい場合、次の反復で、最後の反復と同じ要素をcで取得しますか?(ソート後、新しく追加された要素は現在の要素の前の場所を占めるためです)。
反復中に要素を追加すると、次のイテレータ呼び出しでConcurrentModificationException
がスローされる可能性があります。 TreeSet docsのフェイルファスト動作を参照してください。
要素を繰り返して追加するには、最初に別のセットにコピーします。
TreeSet<Custom> ts = ...
TreeSet<Custom> tsWithExtra = new TreeSet(ts);
for (Custom c : ts) {
// possibly add to tsWithExtra
}
// continue, using tsWithExtra
または、Colinが示唆しているように、反復後にts
とマージする別のコレクションを作成します。
Whileループ内のTreeSetに要素を追加すると、Java.util.ConcurrentModificationExceptionが発生します。
Set<String> ts=new TreeSet<String>();
ts.addAll(Arrays.asList(new String[]{"abb", "abd", "abg"}));
Iterator<String> itr=ts.iterator();
while(itr.hasNext()){
String s = itr.next();
System.out.println("s: " + s);
if (s.equals("abd"))
ts.add("abc");
}
Exception in thread "main" Java.util.ConcurrentModificationException
public static void main(String[] args) {
TreeSet<Integer> ts=new TreeSet<Integer>();
ts.add(2);
ts.add(4);
ts.add(0);
Iterator<Integer> itr=ts.iterator();
while(itr.hasNext()){
Integer c=itr.next();
System.out.println(c);
//Code
ts.add(1);
}
}
Exception in thread "main" Java.util.ConcurrentModificationException
これは、List
、Map
、Set
のようなすべてのコレクションに適用されます。これは、イテレータが起動すると、何らかのロックがかかる可能性があるためです。
イテレータを使用してリストを反復すると、この例外が発生します。そうしないと、要素全体の反復を追加するため、このループは無限大になると思います。
イテレータなしで検討してください。
public static void main(String[] args) {
List<Integer> list=new ArrayList<Integer>();
list.add(2);
list.add(4);
list.add(0);
for (int i = 0; i < 3; i++) {
System.out.println(list.get(i));
list.add(3);
}
System.out.println("Size" +list.size());
}
これで問題ありません。
ConcurrentModificationException
を回避するために、私の UpdateableTreeSet
をチェックすることをお勧めします。ループ中に要素を追加する方法を示す新しい テストケース も追加しました。より正確には、セットの後で延期された更新のために新しい要素をマークします。これは非常にうまく機能します。基本的にあなたは次のようなことをします
for (MyComparableElement element : myUpdateableTreeSet) {
if (someCondition) {
// Add new element (deferred)
myUpdateableTreeSet.markForUpdate(
new MyComparableElement("foo", "bar", 1, 2)
);
}
}
// Perform bulk update
myUpdateableTreeSet.updateMarked();
私はこれがまさにあなたが必要としているものだと思います。 :-)
質問はすでに回答済みですが、最も満足のいく回答は TreeSetのjavadoc 自体にあると思います
このクラスのイテレータメソッドによって返されるイテレータはフェイルファストです。イテレータが作成された後、イテレータ自体のremoveメソッド以外の方法でセットが変更されると、イテレータはConcurrentModificationExceptionをスローします。したがって、同時変更に直面した場合、イテレータは、将来の不確定な時間に任意の非決定論的な動作のリスクを冒すのではなく、迅速かつクリーンに失敗します。
イテレータのフェイルファスト動作は保証できないことに注意してください。一般的に言えば、同期されていない同時変更が存在する場合、ハード保証を行うことは不可能です。フェイルファストイテレータは、ベストエフォートベースでConcurrentModificationExceptionをスローします。したがって、その正確性をこの例外に依存するプログラムを作成するのは誤りです。イテレータのフェイルファスト動作は、バグを検出するためにのみ使用する必要があります。
挿入の実行時に必ず発生する同時変更エラーを回避するために、セットの一時コピーを作成し、代わりにコピーを反復処理して、元のコピーを変更することもできます。
歩行中のConcurrentModificationExceptionを防ぐため。以下は、TreeSet()への高頻度の挿入を許可し、同時にそれを反復できるようにする私のバージョンです。このクラスは、TreeSetが反復しているときに、追加のキューを使用して挿入オブジェクトを格納します。
public class UpdatableTransactionSet {
TreeSet <DepKey> transactions = new TreeSet <DepKey> ();
LinkedList <DepKey> queue = new LinkedList <DepKey> ();
boolean busy=false;
/**
* directly call it
* @param e
*/
void add(DepKey e) {
boolean bb = getLock();
if(bb) {
transactions.add(e);
freeLock();
} else {
synchronized(queue) {
queue.add(e);
}
}
}
/**
* must getLock() and freeLock() while call this getIterator function
* @return
*/
Iterator<DepKey> getIterator() {
return null;
}
synchronized boolean getLock() {
if(busy) return false;
busy = true;
return true;
}
synchronized void freeLock() {
synchronized(queue) {
for(DepKey e:queue) {
transactions.add(e);
}
}
busy = false;
}
}