在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中持有的元素,那么在下一次迭代中,我将在c中获得与上一次迭代中相同的元素吗?(因为排序后,新添加的元素将在当前元素之前占据一个位置)。     

解决方法

        如果在迭代过程中添加元素,则下一个迭代器调用可能会抛出
ConcurrentModificationException
。请参阅TreeSet文档中的快速失败行为。 要迭代和添加元素,可以先复制到另一组:
TreeSet<Custom> ts = ...
TreeSet<Custom> tsWithExtra = new TreeSet(ts);

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

// continue,using tsWithExtra
或创建一个单独的集合,以在迭代后与ѭ3合并,如Colin所建议的。     ,        如果在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();
我想这正是您所需要的。
:-)
    ,        为了防止在行走时发生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   此类的迭代器方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间修改集合,则除了通过迭代器自己的remove方法以外,都可以通过其他方式修改该迭代器,而该迭代器将抛出ConcurrentModificationException 。因此,面对并发修改,迭代器会快速干净地失败,而不会在未来的不确定时间内冒任意,不确定的行为的风险。      请注意,不能保证迭代器的快速失败行为,通常来说,如果存在不同步的并发修改,就不可能做出任何硬性保证。快速失败的迭代器会尽最大努力抛出ConcurrentModificationException。因此,编写依赖于此异常的程序的正确性是错误的:迭代器的快速失败行为仅应用于检测错误。     ,        为了避免在进行插入操作时必然发生的并发修改错误,您还可以创建Set的临时副本,然后遍历该副本,然后修改原始副本。