Использование итератора в TreeSet

СИТУАЦИЯ: у меня есть 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
}

ВОПРОС: Ну, я хочу знать, что если я добавлю новый элемент в TreeSet в цикле while, то этот новый элемент будет отсортирован немедленно. Другими словами, если я добавлю новый элемент в цикле while, и он меньше, чем тот, который я сейчас держу в c, то на следующей итерации я получу тот же элемент в c, что и в последней итерации? ( так как после сортировки вновь добавленный элемент будет занимать место где-то перед текущим элементом).


person aps    schedule 23.06.2011    source источник
comment
Я не показывал компаратор в коде выше.   -  person aps    schedule 24.06.2011
comment
Кроме того, рекомендуется приведение типов IMO Custom c=(Custom)itr.next();, так как возвращаемый тип next()Object.   -  person KNU    schedule 16.01.2015


Ответы (7)


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

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

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

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

// continue, using tsWithExtra

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

person Michael Brewer-Davis    schedule 23.06.2011
comment
Также можно поставить элементы в очередь, которые будут добавлены в другую коллекцию, а затем добавить их все после завершения итерации, а не копировать заранее. - person ColinD; 24.06.2011
comment
Хорошо, тогда, пожалуйста, скажите мне, как сделать следующее: 1. Мне нужна структура данных, которая может поддерживать сортировку. Я использовал TreeSet.Ok? 2. Далее я буду использовать собственный компаратор для TreeSet, так как он состоит из пользовательских объектов. 3. Далее я хочу наложить два набора деревьев на основе значения конкретного объекта. TreeSet состоит из пользовательских объектов, и одним из элементов объекта является время. Если время одного элемента набора деревьев меньше, чем другого, я копирую эту строку в другую. как это сделать? - person aps; 24.06.2011
comment
Спасибо. Но есть ли какой-нибудь элегантный способ наложения двух одинаковых пользовательских элементов TreeSet? У меня есть собственный класс, состоящий из целого числа a, строки b, целого числа c, двойного d. теперь я создал наборы деревьев, содержащие объекты этого пользовательского класса. у меня есть два таких набора деревьев. я хочу пройти через каждый элемент двух наборов деревьев и наложить элементы двух наборов деревьев, в соответствии с которыми у одного есть сущность c меньше. - person aps; 24.06.2011
comment
Я не уверен, что понимаю ваши требования - как узнать, какие два элемента наборов сравнивать? В любом случае, мне кажется, что вы просматриваете два входных набора, чтобы создать третий набор, а не изменяете оригиналы. - person Michael Brewer-Davis; 24.06.2011

Вы получите java.util.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
person anubhava    schedule 23.06.2011

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

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

person NILESH SALPE    schedule 23.06.2011
comment
очевидно, у меня достаточно серого вещества, чтобы понять, что это будет бесконечно... поэтому я бы, очевидно, использовал какое-то условие, на основе которого будут добавляться новые элементы. Но использует ли TreeSet get(i), где i - индекс? я так не думаю. - person aps; 24.06.2011

Чтобы избежать 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();

Думаю, это именно то, что вам нужно. :-)

person kriegaex    schedule 22.02.2013

Чтобы предотвратить 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;
}
}
person Houcheng    schedule 18.03.2014

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

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

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

person KNU    schedule 16.01.2015

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

person interestedparty333    schedule 14.04.2017