簡體   English   中英

在TreeSet上使用迭代器

[英]Using iterator on a TreeSet

情況:我有一個自定義對象的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
}

問題:我想知道如果我在while循環中向TreeSet添加一個新元素,那么新元素會立即被排序。 換句話說,如果我在while循環中添加一個新元素並且它小於我當前在c中持有的元素,那么在下一次迭代中我將獲得與上一次迭代中相同的元素嗎?(因為在排序之后,新添加的元素將占據當前元素之前的某個位置。

如果在迭代期間添加元素,則下一個迭代器調用可能會拋出ConcurrentModificationException 請參閱TreeSet文檔中的故障快速行為。

要迭代和添加元素,您可以先復制到另一個集合:

TreeSet<Custom> ts = ...
TreeSet<Custom> tsWithExtra = new TreeSet(ts);

for (Custom c : ts) {
  // possibly add to tsWithExtra
}

// continue, using tsWithExtra

科林建議,或者在迭代后創建一個單獨的集合與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

這將到達所有集合,如ListMapSet因為當迭代器啟動時它可能會對它進行一些鎖定。

如果使用迭代器迭代列表,則會出現此異常。 我認為否則這個循環將是無限的,因為你添加元素整個迭代。

考慮沒有迭代器:

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();

我想這正是你所需要的。 :-)

在行走時防止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;
}
}

雖然問題已經得到解答,但我認為最令人滿意的答案在於TreeSet本身的javadoc

這個類的迭代器方法返回的迭代器是快速失敗的:如果在創建迭代器之后的任何時候修改了set,​​除了通過迭代器自己的remove方法之外,迭代器將拋出ConcurrentModificationException。 因此,在並發修改的情況下,迭代器快速而干凈地失敗,而不是在未來的未確定時間冒着任意的,非確定性行為的風險。

請注意,迭代器的快速失敗行為無法得到保證,因為一般來說,在存在非同步並發修改的情況下,不可能做出任何硬性保證。 失敗快速迭代器會盡最大努力拋出ConcurrentModificationException。 因此,編寫依賴於此異常的程序以確保其正確性是錯誤的:迭代器的快速失敗行為應該僅用於檢測錯誤。

為了避免在進行插入時必然發生的並發修改錯誤,您還可以創建Set的臨時副本,反而遍歷副本,並修改原始版本。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM