在TreeSet上使用迭代器

情况:我有一个自定义对象的TreeSet,我也使用了自定义比较器。 我已经创建了一个在这个TreeSet上使用的迭代器。

TreeSet ts=new TreeSet(); Iterator 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 ts = ... TreeSet tsWithExtra = new TreeSet(ts); for (Custom c : ts) { // possibly add to tsWithExtra } // continue, using tsWithExtra 

科林建议,或者在迭代后创建一个单独的集合与ts合并。

如果在while循环中向TreeSet中添加元素,则会得到java.util.ConcurrentModificationException

 Set ts=new TreeSet(); ts.addAll(Arrays.asList(new String[]{"abb", "abd", "abg"})); Iterator 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 ts=new TreeSet(); ts.add(2); ts.add(4); ts.add(0); Iterator 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因为当迭代器启动时它可能会对它进行一些锁定。

如果使用迭代器迭代列表,则会出现此exception。 我认为否则这个循环将是无限的,因为你添加元素整个迭代。

考虑没有迭代器:

 public static void main(String[] args) { List list=new ArrayList(); 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  transactions = new TreeSet  (); LinkedList  queue = new LinkedList  (); 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 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。 因此,编写依赖于此exception的程序以确保其正确性是错误的:迭代器的快速失败行为应该仅用于检测错误。

为了避免在进行插入时必然发生的并发修改错误,您还可以创建Set的临时副本,反而遍历副本,并修改原始文件。