Я продолжаю получать java.util.concurrentmodificationexception. Как это исправить?

Я работал над этим фрагментом кода. Вот псевдокод того, что я хочу сделать:

a.проверьте, если размер разделов (который является списком) равен 0.
b.если размер разделов равен 0, то автоматически зачислите учащегося в раздел, вызвав section.add(newSection)
c.else, если разделы size не равен нулю, проверьте наличие конфликтов с расписанием
d.если конфликтов нет, запишите учащегося в секцию, вызвав section.add(newSection)
e.еще ничего не делайте

Java продолжает выдавать мне ошибку «java.util.concurrentmodificationexception». Я знаю, я не должен изменять размер ArrayList при обходе списка, потому что это изменит итератор. Есть ли другой способ решить эту проблему? :D

Большое спасибо. Ваша помощь высоко ценится. :)

 public String enrollsTo(Section newSection){


        StringBuffer result = new StringBuffer();

        String resultNegative = "Failed to enroll in this section.";
        String resultPositive = "Successfully enrolled in section: " + newSection.getSectionName() + ".";

        int previousSectionSize = sections.size();

        if(this.sections.isEmpty()){
            this.sections.add(newSection);
            result.append(resultPositive);
        }else{
            for(Iterator<Section> iterator = sections.iterator(); iterator.hasNext() ; ){
                Section thisSection = iterator.next();

                if(thisSection.conflictsDayWith(newSection)==false &&
                    thisSection.conflictsTimeWith(newSection)==false){
                    this.sections.add(newSection);  //<-- i believe the problem lies here.
                    result.append(resultPositive);
                }
            }
        }
//      if(this.sections.size() == previousSectionSize){
//          result.append(resultNegative);
//      }
        return result.toString();
    }

person 황현정    schedule 03.01.2012    source источник
comment
Примечание: Ваша логика нарушена. Вместо того, чтобы при отсутствии конфликтов вы тестировали каждый раздел, который не конфликтует, добавьте новый раздел.   -  person Howard    schedule 03.01.2012
comment
хм.. Вот почему это кажется немного неправильным и странным. Вы должны указать, должны как-то уточнить это, спасибо, что указали на это. :D   -  person 황현정    schedule 03.01.2012


Ответы (6)


Не делайте sections.add(newSection) внутри цикла for, так как это модификация коллекции, которую вы сейчас перебираете.

Кроме того, разве вы не хотите проверить все разделы, прежде чем решить, добавлять ли newSection или нет? Может быть, что-то вроде этого:

boolean conflict = false;
for (...) {
  if (/* check for conflict */) {
    conflict = true;
    break;
  }
}
if (!conflict) {
  sections.add(newSection);
}
person sudocode    schedule 03.01.2012

Из javadoc для ConcurrentModificationException (выделено мной) :

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

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

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

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

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

person Matthew Farwell    schedule 03.01.2012

Во время повторения коллекции вы не можете изменить ее. Строка this.sections.add(newSection); вызывает исключение. Возможно, вам придется использовать логический маркер, чтобы проверить условие

if(thisSection.conflictsDayWith(newSection)==false &&
                    thisSection.conflictsTimeWith(newSection)==false)

После цикла for, если ваш логический маркер истинен, вы можете написать

 this.sections.add(newSection);  
                    result.append(resultPositive);
person kosa    schedule 03.01.2012

Вы правы в своем предположении,

 this.sections.add(newSection);  

определенно является источником вашей проблемы.

Самое простое решение: иметь логическое значение, представляющее доступность раздела. Начните с предположения, что он доступен. Если в вашем итераторе есть конфликт, установите для него значение false. После итератора добавьте раздел, если он доступен (логическое значение true).

person Sam DeHaan    schedule 03.01.2012

Исключения ConcurrentModificationException часто возникают, когда вы изменяете коллекцию во время перебора ее элементов. Прочтите это руководство для получения более подробной информации, а также этот старый Сообщение SO Почему it.next() генерирует исключение java.util.ConcurrentModificationException?

person Aravind Yarram    schedule 03.01.2012

Я согласен с @sudocode в том, что вы не хотите добавлять новый раздел каждый раз, когда находите даже раздел, который не конфликтует. Я бы подумал, что когда вы пройдёте код в отладчике, это станет очевидным. ;)

Кстати, другой (более неясный) способ сделать это без флага

CHECK: {
  for (...) {
    if (/* check for conflict */) 
      break CHECK;
  }

  sections.add(newSection);
}
person Peter Lawrey    schedule 03.01.2012