ConcurrentModificationException при повторном вставке узла JavaFX [дубликат]

Предлагаю вам также взглянуть на Symmetricds . это библиотека репликации SQLite, доступная для систем Android. вы можете использовать его для синхронизации своей клиентской и серверной базы данных, я также предлагаю иметь отдельные базы данных на сервере для каждого клиента. Попытка хранить данные всех пользователей в одной базе данных mysql не всегда лучшая идея. Особенно, если пользовательские данные будут быстро расти.

1054
задан Raedwald 28 March 2016 в 14:25
поделиться

24 ответа

Iterator.remove() безопасен, вы можете использовать его следующим образом:

List<String> list = new ArrayList<>();

// This is a clever way to create the iterator and call iterator.hasNext() like
// you would do in a while-loop. It would be the same as doing:
//     Iterator<String> iterator = list.iterator();
//     while (iterator.hasNext()) {
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    String string = iterator.next();
    if (string.isEmpty()) {
        // Remove the current element from the iterator and the list.
        iterator.remove();
    }
}

Обратите внимание, что Iterator.remove() является единственным безопасным способом изменять коллекцию во время итерации; поведение не определено, если базовая коллекция модифицируется каким-либо другим способом, пока выполняется итерация.

Источник: docs.oracle> Интерфейс коллекции


Аналогичным образом, если у вас есть ListIterator и вы хотите добавить элементы, вы можете использовать ListIterator#add , по той же причине вы можете использовать [f6 & mdash;] & NBSP; он предназначен для этого.

1470
ответ дан Pragati Singh 15 August 2018 в 22:18
поделиться
  • 1
    Что делать, если вы хотите удалить элемент, отличный от элемента, возвращаемого в текущей итерации? – Eugen 22 April 2013 в 10:51
  • 2
    Вы должны использовать .remove в итераторе, и это только способно удалить текущий элемент, поэтому нет :) – Bill K 22 April 2013 в 19:00
  • 3
    Имейте в виду, что это медленнее по сравнению с использованием ConcurrentLinkedDeque или CopyOnWriteArrayList (по крайней мере, в моем случае) – Dan 24 October 2014 в 02:43
  • 4
    Я использовал для итерации назад по массиву и удаления. – morksinaanab 27 November 2014 в 16:38
  • 5
    Невозможно ли поместить вызов iterator.next() в цикл for? Если нет, может кто-нибудь объяснить, почему? – Blake 12 April 2016 в 13:22

это может быть не лучшим образом, но для большинства небольших случаев это должно быть приемлемым:

"создать второй пустой массив и добавить только те, которые вы хотите keep "

Я не помню, где я это читал ... для справедливости я сделаю эту вики в надежде, что кто-то найдет ее или просто не заработает репутацию Я не заслуживаю.

-3
ответ дан ajax333221 15 August 2018 в 22:18
поделиться

С помощью Java 8 вы можете использовать новый removeIf метод . Применимо к вашему примеру:

Collection<Integer> coll = new ArrayList<Integer>();
//populate

coll.removeIf(i -> i.intValue() == 5);
156
ответ дан assylias 15 August 2018 в 22:18
поделиться
  • 1
    Ооооо! Я надеялся, что что-то в Java 8 или 9 может помочь. Это все еще кажется довольно многословным, но мне все еще нравится. – James T Snell 23 September 2015 в 18:25
  • 2
    Использует ли equals () в этом случае тоже? – Anmol Gupta 11 December 2015 в 09:57
  • 3
    кстати, removeIf использует петли Iterator и while. Вы можете увидеть его в java 8 java.util.Collection.java – omerhakanbilici 31 October 2016 в 15:10
  • 4
    @omerhakanbilici Некоторые реализации, такие как ArrayList, переопределяют его по соображениям производительности. Тот, о котором вы говорите, является только реализацией по умолчанию. – Didier L 31 March 2017 в 08:33
Collection<Integer> l = new ArrayList<Integer>();//Do the collection thing...

l.removeIf(i -> i == 5);      //iterates through the collection and removes every occurence of 5

Лямбда-выражения и методы сбора в Jdk 8 входят в Handy и добавляют некоторый синтаксический сахар

0
ответ дан Barnabas Ukwuani 15 August 2018 в 22:18
поделиться
  • 1
    Хотя этот фрагмент кода может быть решением, , включая объяснение , действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин вашего предложения кода. – Narendra Jadhav 19 July 2018 в 06:15
  • 2
    Спасибо, скорректировано. – Barnabas Ukwuani 19 July 2018 в 15:12

С Коллекции Eclipse (ранее коллекции GS ) будет работать метод removeIf, определенный на MutableCollection :

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.lessThan(3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

С синтаксисом Java 8 Lambda это можно записать следующим образом:

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.cast(integer -> integer < 3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

Необходим вызов Predicates.cast(), потому что по умолчанию removeIf метод был добавлен на интерфейс java.util.Collection в Java 8.

Примечание: я являюсь коммиттером для коллекций Eclipse .

11
ответ дан bruno 15 August 2018 в 22:18
поделиться

Это работает:

Iterator<Integer> iter = l.iterator();
while (iter.hasNext()) {
    if (iter.next().intValue() == 5) {
        iter.remove();
    }
}

Я предположил, что поскольку цикл foreach является синтаксическим сахаром для итерации, использование итератора не помогло бы ... но оно дает вам эту функциональность .remove().

320
ответ дан Christian Neverdal 15 August 2018 в 22:18
поделиться
  • 1
    foreach loop является синтаксическим сахаром для итерации. Однако, как вы указали, вам нужно вызвать remove на итераторе, который foreach не дает вам доступа. Следовательно, причина, по которой вы не можете удалить в цикле foreach (даже если вы фактически используете итератор под капотом) – madlep 22 October 2008 в 00:30
  • 2
    +1, например, код для использования iter.remove () в контексте, который не имеет ответа Билла К. [непосредственно]. – Eddified 3 October 2012 в 18:01
  • 3
    Я удаляю итератор, как вы делали здесь, но я все равно продолжаю получать такую ​​же ошибку. Есть идеи? – Gokhan Arik 6 May 2014 в 02:49

В дополнение к @assylias answer вы также можете использовать новый поток api, если вы используете Java 8:

List<Integer> l = Arrays.asList(4, 5, 6);

static boolean condition(Integer i) {
    return i == 5;
}

static Predicate<Integer> predicate = YourClassName::condition;

l.stream()
    .filter(predicate.negate())
    .forEach(System.out::println);

Если вы инвертируете условие, решение становится еще более кратким, поскольку вам не нужно negate() предикат, что позволяет использовать только ссылку на метод:

List<Integer> l = Arrays.asList(4, 5, 6);

static boolean condition(Integer i) {
    return i != 5;    // <-- condition has been negated
}

l.stream()
    .filter(YourClassName::condition)
    .forEach(System.out::println);

Один из красотой этого является то, что поток лениво оценивается, т. е. операция filter() фактически не оценивается до тех пор, пока она не будет использована терминальной операцией, такой как forEach(). Подробнее об этом можно найти в учебнике для Oracle .

14
ответ дан Community 15 August 2018 в 22:18
поделиться
  • 1
    Это только отфильтровывает элементы, которые в конечном итоге либо возвращаются, либо потребляются через побочные эффекты. Он не изменяет существующий, фактический список, который, я думаю, является тем, что предназначено. Этот ответ фактически описывает, как элемент можно удалить с помощью Java 8. – Makoto 16 September 2015 в 23:11
List<String> strings=new ArrayList<String>(){};

while(strings.size() > 0) {

 String str = strings.remove(0);
}
-1
ответ дан developer747 15 August 2018 в 22:18
поделиться
  • 1
    Этот вопрос касается удаления какого-либо определенного элемента из списка, а не его полного удаления, поэтому это не отвечает на вопрос. – Radiodef 9 August 2018 в 00:24

Поскольку вопрос уже ответил, то лучший способ - использовать метод удаления объекта итератора, я хотел бы перейти к особенностям места, где была выбрана ошибка "java.util.ConcurrentModificationException".

Каждый класс коллекции имеет частный класс, который реализует интерфейс Iterator и предоставляет такие методы, как next(), remove() и hasNext().

Следующий код выглядит примерно так:

public E next() {
    checkForComodification();
    try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
    } catch(IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
    }
}

Здесь метод checkForComodification реализован как

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

Итак, как вы можете видеть, если вы явно попытаетесь удалить элемент из коллекции. Это приводит к тому, что modCount отличается от expectedModCount, что приводит к исключению ConcurrentModificationException.

38
ответ дан i_am_zero 15 August 2018 в 22:18
поделиться
  • 1
    Очень интересно. Спасибо! Я часто сам не вызываю remove (), вместо этого предпочитаю очищать коллекцию после итерации через нее. Не сказать, что это хорошая модель, именно то, что я делал в последнее время. – James T Snell 23 September 2015 в 18:27

Лучшим способом (рекомендуется) является использование пакета java.util.Concurrent. Используя этот пакет, вы можете легко избежать этого исключения. см. Модифицированный код

public static void main(String[] args) {
        Collection<Integer> l = new CopyOnWriteArrayList<Integer>();

        for (int i=0; i < 10; ++i) {
            l.add(new Integer(4));
            l.add(new Integer(5));
            l.add(new Integer(6));
        }

        for (Integer i : l) {
            if (i.intValue() == 5) {
                l.remove(i);
            }
        }

        System.out.println(l);
    }
1
ответ дан jagdish khetre 15 August 2018 в 22:18
поделиться

A ListIterator позволяет добавлять или удалять элементы в списке. Предположим, у вас есть список объектов Car:

List<Car> cars = ArrayList<>();
// add cars here...

for (ListIterator<Car> carIterator = cars.listIterator();  carIterator.hasNext(); )
{
   if (<some-condition>)
   { 
      carIterator().remove()
   }
   else if (<some-other-condition>)
   { 
      carIterator().add(aNewCar);
   }
}
1
ответ дан james.garriss 15 August 2018 в 22:18
поделиться

Люди утверждают, что нельзя удалить из коллекции, которая повторяется в цикле foreach. Я просто хотел указать, что это технически неверно и точно описывает (я знаю, что вопрос OP настолько продвинут, чтобы избежать этого, чтобы понять это) код, лежащий в основе этого предположения:

    for (TouchableObj obj : untouchedSet) {  // <--- This is where ConcurrentModificationException strikes
        if (obj.isTouched()) {
            untouchedSet.remove(obj);
            touchedSt.add(obj);
            break;  // this is key to avoiding returning to the foreach
        }
    }

Не исключено, что вы не сможете удалить из итерированного Colletion, а затем продолжить, после чего продолжить. Следовательно, break в коде выше.

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

4
ответ дан John 15 August 2018 в 22:18
поделиться

В таких случаях общий трюк (был?) для возврата назад:

for(int i = l.size() - 1; i >= 0; i --) {
  if (l.get(i) == 5) {
    l.remove(i);
  }
}

Тем не менее, я более чем счастлив, что у вас есть лучшие способы в Java 8, например. removeIf или filter в потоках.

17
ответ дан Landei 15 August 2018 в 22:18
поделиться
  • 1
    Это хороший трюк. Но он не будет работать с неиндексированными коллекциями, такими как наборы, и было бы очень медленно говорить о связанных списках. – Claudiu 29 August 2014 в 17:12
  • 2
    @Claudiu Да, это определенно только для ArrayList s или подобных коллекций. – Landei 30 August 2014 в 16:47
  • 3
    Я использую ArrayList, это отлично работает, спасибо. – StarSweeper 21 February 2018 в 07:30
  • 4
    индексы велики. Если это так часто, почему вы не используете for(int i = l.size(); i-->0;) {? – John 27 April 2018 в 20:20

С традиционным для цикла

ArrayList<String> myArray = new ArrayList<>();

   for (int i = 0; i < myArray.size(); ) {
        String text = myArray.get(i);
        if (someCondition(text))
             myArray.remove(i);
        else 
             i++;
      }
6
ответ дан Lluis Felisart 15 August 2018 в 22:18
поделиться

Вы можете перебирать список, используя for-loop, и вам нужно вызвать list.remove (0). Вам нужно с жестким кодом индексировать индексный параметр remove с нулем. См. Также этот ответ :

List<Integer> list = new ArrayList<Integer>();

list.add(1);
list.add(2);
list.add(3);
list.add(4);
int list_size = list.size();
for (int i = 0; i < list_size; i++) {
    list.remove(0);
}
-1
ответ дан Markus Pscheidt 15 August 2018 в 22:18
поделиться
  • 1
    Вопрос касается цикла foreach. Вы должны проверить их :) – Gibolt 23 March 2018 в 16:30
  • 2
    Это пустая трата времени. list.clear(). – Sedrick 1 August 2018 в 19:06
  • 3
    Этот вопрос касается удаления какого-либо определенного элемента из списка, а не его полного удаления, поэтому это не отвечает на вопрос. – Radiodef 9 August 2018 в 00:23

В случае ArrayList: remove (int index) - если (индекс - позиция последнего элемента), он избегает без System.arraycopy() и не занимает времени для этого.

Время arraycopy увеличивается, если (индекс уменьшается), кстати, элементы списка также уменьшаются!

лучший эффективный способ удаления - удаление его элементов в порядке убывания: while(list.size()>0)list.remove(list.size()-1); // принимает O (1) while(list.size()>0)list.remove(0); // принимает O (факториал ( n))

//region prepare data
ArrayList<Integer> ints = new ArrayList<Integer>();
ArrayList<Integer> toRemove = new ArrayList<Integer>();
Random rdm = new Random();
long millis;
for (int i = 0; i < 100000; i++) {
    Integer integer = rdm.nextInt();
    ints.add(integer);
}
ArrayList<Integer> intsForIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsDescIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsIterator = new ArrayList<Integer>(ints);
//endregion

// region for index
millis = System.currentTimeMillis();
for (int i = 0; i < intsForIndex.size(); i++) 
   if (intsForIndex.get(i) % 2 == 0) intsForIndex.remove(i--);
System.out.println(System.currentTimeMillis() - millis);
// endregion

// region for index desc
millis = System.currentTimeMillis();
for (int i = intsDescIndex.size() - 1; i >= 0; i--) 
   if (intsDescIndex.get(i) % 2 == 0) intsDescIndex.remove(i);
System.out.println(System.currentTimeMillis() - millis);
//endregion

// region iterator
millis = System.currentTimeMillis();
for (Iterator<Integer> iterator = intsIterator.iterator(); iterator.hasNext(); )
    if (iterator.next() % 2 == 0) iterator.remove();
System.out.println(System.currentTimeMillis() - millis);
//endregion
  • для индексной петли: 1090 мс
  • для индекса desc: 519 мсек --- лучший
  • для итератора: 1043 мсек
0
ответ дан Nurlan 15 August 2018 в 22:18
поделиться

Сделать копию существующего списка и перебрать новую копию.

for (String str : new ArrayList<String>(listOfStr))     
{
    listOfStr.remove(/* object reference or index */);
}
6
ответ дан Priyank Doshi 15 August 2018 в 22:18
поделиться
  • 1
    Создание копии звучит как пустая трата ресурсов. – Antzi 21 August 2013 в 13:35
  • 2
    @Antzi Это зависит от размера списка и плотности объектов внутри. Еще ценное и действенное решение. – mre 4 May 2016 в 14:14

Вы можете либо использовать итератор прямо так, как вы упомянули, либо сохранить вторую коллекцию и добавить каждый элемент, который хотите удалить, в новую коллекцию, а затем удалитьAll в конце. Это позволяет вам использовать безопасность типа для каждого цикла за счет увеличения использования памяти и времени процессора (не должно быть огромной проблемой, если у вас нет действительно больших списков или действительно старого компьютера)

public static void main(String[] args)
{
    Collection<Integer> l = new ArrayList<Integer>();
    Collection<Integer> itemsToRemove = new ArrayList<Integer>();
    for (int i=0; i < 10; ++i) {
    l.add(new Integer(4));
    l.add(new Integer(5));
    l.add(new Integer(6));
    }
    for (Integer i : l)
    {
        if (i.intValue() == 5)
            itemsToRemove.add(i);
    }

    l.removeAll(itemsToRemove);
    System.out.println(l);
}
22
ответ дан RodeoClown 15 August 2018 в 22:18
поделиться
  • 1
    это то, что я обычно делаю, но явный итератор - это более элегантное решение, которое я чувствую. – Claudiu 22 October 2008 в 00:51
  • 2
    Достаточно справедливо, если вы не делаете ничего с итератором - при его открытии упрощается делать такие вещи, как вызов .next () дважды за цикл и т. Д. Не большая проблема, но может вызвать проблемы, если вы делаете что-то более сложное, чем просто просмотр списка для удаления записей. – RodeoClown 22 October 2008 в 00:58
  • 3
    @RodeoClown: в исходном вопросе Claudiu удаляет из коллекции, а не итератор. – matt b 22 October 2008 в 15:29
  • 4
    Удаление из итератора удаляется из основной коллекции ... но то, что я говорил в последнем комментарии, заключается в том, что если вы делаете что-то более сложное, чем просто искать удаленные в цикле (например, обрабатывать правильные данные), используя итератор, можете сделать некоторые ошибки легче сделать. – RodeoClown 22 October 2008 в 19:35
  • 5
    Если это простые значения удаления, которые не нужны, и цикл делает только одно, использование итератора напрямую и вызов .remove () абсолютно прекрасен. – RodeoClown 22 October 2008 в 19:36
for (Integer i : l)
{
    if (i.intValue() == 5){
            itemsToRemove.add(i);
            break;
    }
}

Ловушка - это удаление элемента из списка, если вы пропустите внутренний вызов iterator.next (). он все еще работает! Хотя я и не предлагаю писать такой код, он помогает понять концепцию: -)

Cheers!

0
ответ дан Srinivasan Thoyyeti 15 August 2018 в 22:18
поделиться

У меня есть предложение по указанной выше проблеме. Нет необходимости в дополнительном списке или дополнительном времени. Пожалуйста, найдите пример, который будет делать одни и те же вещи, но по-другому.

//"list" is ArrayList<Object>
//"state" is some boolean variable, which when set to true, Object will be removed from the list
int index = 0;
while(index < list.size()) {
    Object r = list.get(index);
    if( state ) {
        list.remove(index);
        index = 0;
        continue;
    }
    index += 1;
}

Это позволит избежать исключения параллелизма.

0
ответ дан svick 15 August 2018 в 22:18
поделиться
  • 1
    В вопросе явно указано, что ОП не требуется с помощью ArrayList и, следовательно, не может полагаться на get(). В противном случае, вероятно, хороший подход. – kaskelotti 13 April 2014 в 12:09

ConcurrentHashMap или ConcurrentLinkedQueue или ConcurrentSkipListMap может быть другой опцией, потому что они никогда не будут бросать любое ConcurrentModificationException, даже если вы удалите или добавите элемент.

0
ответ дан Yessy 15 August 2018 в 22:18
поделиться
14
ответ дан Community 5 September 2018 в 22:03
поделиться
-1
ответ дан pedram bashiri 29 October 2018 в 05:09
поделиться
0
ответ дан Yazon2006 29 October 2018 в 05:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: