Используя итератор на 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 на основе максимальных усилий. Поэтому было бы неправильно написать программу, которая зависела от этого исключения для его корректность: быстрое поведение итераторов должно использоваться только для обнаружения ошибок.
чтобы избежать ошибки параллельной модификации, которая обязательно произойдет при вставке, вы также можете создать временную копию набора, выполнить итерацию через копию и изменить оригинал.