在 TreeSet 上使用迭代器

发布于 2024-11-17 01:26:36 字数 494 浏览 9 评论 0原文

情况:我有一个自定义对象的 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 中获得与上一次迭代相同的元素吗?(因为排序后,新添加的元素将占据当前元素之前的某个位置)。

SITUATION: I have a TreeSet of custom Objects and I have also used a custom Comparator. I have created an iterator to use on this 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: Well I want to know that if I add a new element to the TreeSet within the while loop, then will that new element get sorted immediately. In other words, if I add a new element within the while loop and it is less than the one which I am currently holding in c, then in the next iteration will I be getting the same element in c as in the last iteration?(since after sorting, the newly added element will occupy a place somewhere before the current element).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

心如荒岛 2024-11-24 01:26:36

如果您在迭代期间添加元素,则下一次迭代器调用可能会抛出 ConcurrentModificationException。请参阅 TreeSet 文档中的快速失败行为。

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

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

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

// continue, using tsWithExtra

或者创建一个单独的集合,以便在迭代后与 ts 合并,正如 Colin 建议的那样。

If you add an element during your iteration, your next iterator call will likely throw a ConcurrentModificationException. See the fail-fast behavior in TreeSet docs.

To iterate and add elements, you could copy first to another set:

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

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

// continue, using tsWithExtra

or create a separate collection to be merged with ts after iteration, as Colin suggests.

沉鱼一梦 2024-11-24 01:26:36

如果您在 while 循环内将元素添加到 TreeSet 中,您将得到一个 java.util.ConcurrentModificationException

Set<String> ts = new TreeSet<>();
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

You will get a java.util.ConcurrentModificationException if you add an element into the TreeSet inside while loop.

Set<String> ts = new TreeSet<>();
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");
}

###Output

Exception in thread "main" java.util.ConcurrentModificationException
仙气飘飘 2024-11-24 01:26:36
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());
}

这样就可以了。

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

This will come to all collections like List , Map , Set
Because when iterator starts it may be putting some lock on it .

if you iterate list using iterator then this exception will come. I think otherwise this loop will be infinite as you are adding element whole iterating.

Consider without iterator:

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

this will be fine .

乞讨 2024-11-24 01:26:36

为了避免 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();

我想这正是你所需要的。 <代码>:-)

In order to avoid the ConcurrentModificationException you might want to check out my UpdateableTreeSet. I have even added a new test case showing how to add elements during a loop. To be more exact, you mark new elements for a later, deferred update of the set. This works quite nicely. Basically you do something like

for (MyComparableElement element : myUpdateableTreeSet) {
    if (someCondition) {
        // Add new element (deferred)
        myUpdateableTreeSet.markForUpdate(
            new MyComparableElement("foo", "bar", 1, 2)
        );
    }
}

// Perform bulk update
myUpdateableTreeSet.updateMarked();

I guess this is quite exactly what you need. :-)

情魔剑神 2024-11-24 01:26:36

防止行走时出现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;
}
}

To prevent the ConcurrentModificationException while walking.
Below is my version to allow high frequency insertion into the TreeSet() and allow concurrently iterate on it. This class use a extra queue to store the inserting object when the TreeSet is being iterating.

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;
}
}
海之角 2024-11-24 01:26:36

虽然问题已经得到解答,但我认为最令人满意的答案在于 TreeSet 本身的 javadoc

此类的迭代器方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间修改集合,除了通过迭代器自己的删除方法之外的任何方式,迭代器都会抛出 ConcurrentModificationException。因此,面对并发修改,迭代器会快速而干净地失败,而不是在未来不确定的时间冒任意、非确定性行为的风险。

请注意,迭代器的快速失败行为无法按原样保证,>一般来说,在存在不同步并发修改的情况下不可能做出任何硬保证。快速失败迭代器会尽力抛出 ConcurrentModificationException。因此,编写依赖于此异常来确保其正确性的程序是错误的:迭代器的快速失败行为应该仅用于检测错误。

While the question has already been answered, I think the most satisfactory answer lies in javadoc of TreeSet itself

The iterators returned by this class's iterator method are fail-fast: if the set is modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, >generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

风渺 2024-11-24 01:26:36

为了避免在执行插入时必然发生的并发修改错误,您还可以创建 Set 的临时副本,改为迭代副本,然后修改原始副本。

To avoid the concurrent modification error that's bound to occur when you're doing the insertion, you could also create a temporary copy of the Set, iterate through the copy instead, and modify the original.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文