Используя итератор на 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, то этот новый элемент будет немедленно отсортирован. Другими словами, если я добавляю новый элемент в цикл while и он меньше того, который я сейчас держу в c, то в следующем итерация будет ли я получать тот же элемент в c, что и на последней итерации?(так как после сортировки, вновь добавленный элемент будет занимать место где-то перед текущим элементом).

7 ответов


Если вы добавите элемент во время итерации, ваш следующий вызов итератора, скорее всего, бросит ConcurrentModificationException. См. поведение fail-fast в TreeSet документы.

для итерации и добавления элементов вы можете скопировать сначала в другой набор:

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

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

// continue, using tsWithExtra

или создать отдельную коллекцию для слияния с ts после итерации, как Колин говорит.


Вы получаете java.утиль.ConcurrentModificationException если вы добавляете элемент в TreeSet внутри цикла while.

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 Потому что, когда итератор запускается, он может наложить на него некоторую блокировку .

если вы повторяете список с помощью 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());
}

это будет хорошо .


во избежание 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 () и разрешить одновременную итерацию на нем. Этот класс использует дополнительную очередь для хранения объекта вставки при итерации набора деревьев.

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

хотя на вопрос уже дан ответ, я думаю, что наиболее удовлетворительный ответ заключается в javadoc из TreeSet

Iterators, возвращенные методом iterator этого класса являются сбоем быстро: если набор изменяется, в любое время после создания итератора, всегда кроме через собственный метод remove iterator, то это, итератор бросит ConcurrentModificationException. Таким образом, перед лицом параллельной модификации итератор быстро терпит неудачу и чисто, а не рисковать произвольным, недетерминированным поведением в неопределенное время в будущем.

обратите внимание, что поведение сбоя быстро iterator не может быть гарантировано, так как это, вообще говоря, невозможно сделать никаких жестких гарантий в присутствии несинхронизированные параллельной модификации. Сбой быстро Iterators бросать ConcurrentModificationException на основе максимальных усилий. Поэтому было бы неправильно написать программу, которая зависела от этого исключения для его корректность: быстрое поведение итераторов должно использоваться только для обнаружения ошибок.


чтобы избежать ошибки параллельной модификации, которая обязательно произойдет при вставке, вы также можете создать временную копию набора, выполнить итерацию через копию и изменить оригинал.