ArrayList Failfast ConcurrentModificationException [дубликат]

Вы можете сделать это с событиями перехода, так что вы так и есть, вы создаете 2 класса css для перехода, один из которых содержит анимацию другой, удерживая дисплей в состоянии none. и вы переключаете их после окончания анимации? В моем случае я могу отображать div снова, если я нажимаю btn и удаляю оба класса.

Попробуйте отрезать ниже ...

$(document).ready(function() {
  //assign transition event
  $("table").on("animationend webkitAnimationEnd", ".visibility_switch_off", function(event) {
    //we check if this is the same animation we want  
    if (event.originalEvent.animationName == "col_hide_anim") {
      //after the animation we assign this new class that basically hides the elements.    
      $(this).addClass("animation-helper-display-none");
    }

  });

  $("button").click(function(event) {

    $("table tr .hide-col").toggleClass(function() {
      //we switch the animation class in a toggle fashion...
      //and we know in that after the animation end, there is will the animation-helper-display-none extra class, that we nee to remove, when we want to show the elements again, depending on the toggle state, so we create a relation between them.
      if ($(this).is(".animation-helper-display-none")) {
        //im toggleing and there is already the above classe, then what we want it to show the elements , so we remove both classes...        
        return "visibility_switch_off animation-helper-display-none";
      } else {
        //here we just want to hide the elements, so we just add the animation class, the other will be added later be the animationend event...        
        return "visibility_switch_off";
      }

    });

  });

});
table th {
  background-color: grey;
}

table td {
  background-color: white;
  padding:5px;
}

.animation-helper-display-none {
  display: none;
}

table tr .visibility_switch_off {
  animation-fill-mode: forwards;
  animation-name: col_hide_anim;
  animation-duration: 1s;
}

@-webkit-keyframes col_hide_anim {
  0% {opacity: 1;}
  100% {opacity: 0;}
}

@-moz-keyframes col_hide_anim {
  0% {opacity: 1;}
  100% {opacity: 0;}
}

@-o-keyframes col_hide_anim {
  0% {opacity: 1;}
  100% {opacity: 0;}
}

@keyframes col_hide_anim {
  0%   {opacity: 1;}
  100% {opacity: 0;}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
  <theader>
    <tr>
      <th>Name</th>
      <th class='hide-col'>Age</th>
      <th>Country</th>
    </tr>
  </theader>
  <tbody>
    <tr>
      <td>Name</td>
      <td class='hide-col'>Age</td>
      <td>Country</td>
    </tr>
  </tbody>
</table>

<button>Switch - Hide Age column with fadeout animation and display none after</button>

17
задан Kevin J. Chase 25 November 2015 в 12:03
поделиться

5 ответов

Короткий ответ

Поскольку отказоустойчивое поведение итератора не гарантируется.

Длинный ответ

Вы получаете это исключение, потому что вы

Плохо:

// we're using iterator
for (Iterator<String> i = c.iterator(); i.hasNext();) {  
    // here, the collection will check it hasn't been modified (in effort to fail fast)
    String s = i.next();
    if(s.equals("lalala")) {
        // s is removed from the collection and the collection will take note it was modified
        c.remove(s);
    }
}

Хорошо:

// we're using iterator
for (Iterator<String> i = c.iterator(); i.hasNext();) {  
    // here, the collection will check it hasn't been modified (in effort to fail fast)
    String s = i.next();
    if(s.equals("lalala")) {
        // s is removed from the collection through iterator, so the iterator knows the collection changed and can resume the iteration
        i.remove();
    }
}

Теперь, чтобы перейти к следующему элементу, «почему»: в приведенном выше коде обратите внимание на то, как выполняется проверка модификации - удаление помещает коллекцию как измененную, а следующая итерация проверяет любые изменения и терпит неудачу, если она обнаруживает изменение коллекции. Еще одна важная вещь: ArrayList (не уверенный в других сборниках) делает not проверкой на модификацию в hasNext().

Поэтому могут произойти две странные вещи:

  • Если вы удалите последний элемент во время итерации, ничего не будет выбрано. Это потому, что нет «следующего» элемента, поэтому итерация заканчивается до достижения кода проверки модификации
  • . Если вы удалите второй-последний элемент ArrayList.hasNext() будет также возвращать false, потому что итератор current index теперь указывает на последний элемент (бывший второй по-последнему). Таким образом, даже в этом случае после удаления нет «следующего» элемента

Обратите внимание, что все это соответствует документации ArrayList :

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

Отредактировано для добавления:

Этот вопрос дает некоторую информацию о том, почему проверка параллельной модификации не выполнена в hasNext() и выполняется только в next().

]
22
ответ дан Community 26 August 2018 в 09:57
поделиться

Если вы посмотрите на исходный код для итератора ArrayList (частный вложенный класс Itr), вы увидите недостаток в коде.

Код должен быть с ошибкой, быстрый, который делается внутри итератора внутри, вызывая checkForComodification(), однако hasNext() не делает этот вызов, вероятно, по соображениям производительности.

Вместо hasNext():

public boolean hasNext() {
    return cursor != size;
}

Это означает, что когда вы находитесь в элементе second last в списке, а затем удалите элемент (любой элемент), размер уменьшается, а hasNext() думает, что вы 're на последнем элементе (которого вы не были) и возвращает false, пропуская итерацию последнего элемента без ошибок.

OOPS !!!!

4
ответ дан Andreas 26 August 2018 в 09:57
поделиться

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

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
    at java.util.ArrayList$Itr.next(Unknown Source)
    at com.ii4sm.controller.Evil.removeLalala(Evil.java:23)
    at com.ii4sm.controller.Evil.main(Evil.java:17)

В stacktrace очевидно, что строка i.next(); выдает ошибку. Но когда у вас есть только два элемента в коллекции.

Collection<String> c = new ArrayList<String>();
c.add("lalala");
c.add("lalala");
removeLalala(c);
System.err.println(c);

Когда первый удаляется i.hasNext() возвращает false, а i.next() никогда не выполняется, чтобы выбросить исключение

3
ответ дан awsome 26 August 2018 в 09:57
поделиться

вы должны удалить из iterator (i), а не collection (c) напрямую;

попробуйте это:

for (Iterator<String> i = c.iterator(); i.hasNext();) {
    String s = i.next();
    if(s.equals("lalala")) {
        i.remove(); //note here, changing c to  i with no parameter.
    }
}

EDIT:

Причина, по которой первая попытка вызывает исключение, а вторая - нет, просто из-за количества элементов в вашей коллекции.

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

2
ответ дан nafas 26 August 2018 в 09:57
поделиться

Вы не можете удалить из списка, если вы просматриваете его с циклом «для каждого».

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

Если вы используете код ниже, вы не получите никакого исключения:

private static void removeLalala(Collection<String> c) 
  {
    /*for (Iterator<String> i = c.iterator(); i.hasNext();) {
      String s = i.next();
      if(s.equals("lalala")) {
        c.remove(s);
      }
    }*/

    Iterator<String> it = c.iterator();
    while (it.hasNext()) {
        String st = it.next();
        if (st.equals("lalala")) {
            it.remove();
        }
    }
  }
1
ответ дан Shiladittya Chakraborty 26 August 2018 в 09:57
поделиться
Другие вопросы по тегам:

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