情况:我有自定义对象的TreeSet,我也使用了自定义Comparator。我已经创建了一个在这个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中保存的元素,那么在下一次迭代中我将获得与上一次迭代中相同的元素吗?(因为在排序之后,新添加的元素将占据当前元素之前的某个位置。
答案 0 :(得分:23)
如果在迭代期间添加元素,则下一个迭代器调用可能会抛出ConcurrentModificationException
。请参阅TreeSet文档中的快速失败行为。
要迭代和添加元素,您可以先复制到另一个集合:
TreeSet<Custom> ts = ...
TreeSet<Custom> tsWithExtra = new TreeSet(ts);
for (Custom c : ts) {
// possibly add to tsWithExtra
}
// continue, using tsWithExtra
或创建一个单独的集合,以便在迭代后与ts
合并,正如Colin建议的那样。
答案 1 :(得分:6)
如果在循环中向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
答案 2 :(得分:3)
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());
}
这没关系。
答案 3 :(得分:1)
为了避免ConcurrentModificationException
您可能想要查看我的UpdateableTreeSet
。我甚至添加了一个新的test case,展示了如何在循环中添加元素。更确切地说,您为该集合的延迟更新标记新元素。这很好用。基本上你做的事情就像
for (MyComparableElement element : myUpdateableTreeSet) {
if (someCondition) {
// Add new element (deferred)
myUpdateableTreeSet.markForUpdate(
new MyComparableElement("foo", "bar", 1, 2)
);
}
}
// Perform bulk update
myUpdateableTreeSet.updateMarked();
我想这正是你所需要的。 :-)
答案 4 :(得分:0)
防止行走时出现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;
}
}
答案 5 :(得分:0)
虽然这个问题已经得到解答,但我认为最令人满意的答案在于javadoc of TreeSet本身
此类的迭代器方法返回的迭代器是快速失败的:如果在创建迭代器之后的任何时间修改集合,除了通过迭代器自己的删除方法之外,迭代器将抛出ConcurrentModificationException。因此,面对并发修改,迭代器会快速而干净地失败,而不是在未来不确定的时间冒着任意的,非确定性行为的风险。
请注意,迭代器的快速失败行为不能保证,因为一般来说,在存在非同步并发修改的情况下,不可能做出任何硬保证。失败快速迭代器会尽最大努力抛出ConcurrentModificationException。因此,编写依赖于此异常的程序以确保其正确性是错误的:迭代器的快速失败行为应仅用于检测错误。
答案 6 :(得分:0)
为了避免在进行插入时必然会发生的并发修改错误,您还可以创建Set的临时副本,反而遍历副本,并修改原始文件。