2017-02-15 11 views
1

В настоящее время я работаю над проектом класса типа Software Engineering и запускаю то, что я вижу как странную ошибку. Согласно исследованию/отладке, что я сделал Java ConcurrentModificationException обычно генерируется, когда HashMap модифицируется к тому времени, когда вызывается его метод next().Исключение одновременной модификации Несмотря на то, что HashMap восстанавливается до исходного состояния?

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

  1. Получить все ключи HashMap (доска)
  2. для (Координировать ключевые: ключи) ...
  3. Move Кусок обнаружить на борту
  4. Проверьте король находится под атакой
  5. Move штучных назад
  6. Loop

Этот раздел кода генерирует ConcurrentModificationException при циклизации, несмотря на то, что HashMap находится в том же состоянии, в котором он начинался, когда начиналась итерация - размер и точные значения.

Любые идеи, что может вызвать это?

Loop:

for(ValidatorCoordinate key: keys){ 
    if(board.findPiece(key).getPieceType() != XiangqiPieceType.GENERAL 
     && moveValid(key, spaceBetween, color) 
     && tryBlock(key, spaceBetween, dst, color)){ 
       return true; 
    } 
} 

tryBlock() (метод, где Модифицированный HashMap)

private boolean tryBlock(ValidatorCoordinate source, 
    ValidatorCoordinate destination, 
    ValidatorCoordinate underAttack, XiangqiColor c){ 

     board.movePiece(source, destination, c); 
     boolean blocked = !underAttack(underAttack, c); 
     board.movePiece(destination, source, c); 
     return blocked; 
} 

movePiece() метод определенно перемещает куски в HashMap (тщательно протестированы в этой точке)

Любая помощь была бы принята с благодарностью.

Спасибо!

Редактировать: Для пояснения этот общий подход работал до тех пор, пока я не отвлек движение до другого метода (tryBlock). Ранее содержимое цикла tryBlock находилось внутри цикла, и исключение не было выбрано. Вот почему я беспокоюсь об этом, так как для меня это тоже должно было исключить исключение.

+1

Любая модификация отклоняется 'HashMap', даже если она заканчивается возвратом в исходное состояние. (По уважительной причине, даже если содержимое карты одинаково, внутреннее расположение хэш-кодов может отличаться.) –

+0

Просто, чтобы уточнить, есть ли причина, по которой это исключение не будет выбрано до тех пор, пока не зациклится? Модификация технически разрешена, но петля жалуется. Кроме того, этот же подход работал ранее, однако перемещение части не было абстрагировано от другого метода в этот момент. Эта ошибка появилась только при рефакторинге. Спасибо! – bwbonanno

+0

Методы «Итераторы», как правило, те, что вызывают исключение ConcurrentModificationException. Методы модификации не знают, что итерация продолжается. –

ответ

0

Рассматривали ли вы переход с HashMap на ConcurrentHashMap? Последний позволяет параллельным считываниям на карте и избегает многих исключений параллелизма, которые часто возникают из-за многопоточного кода, совместно использующего карту (не совсем то, что вы описываете).

Как указал комментатор, даже если вы инвертируете вещи (например, добавьте/удалите или удалите/добавьте), вы, скорее всего, получите исключения параллелизма.

+0

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

0

Проблема возникает из-за того, что вы повторяете набор ключей карты и изменяете карту в цикле. Карта не отслеживает тот факт, что вы возвращаете ее в том же состоянии. Любая структурная модификация карты аннулирует существующие итераторы.

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

for (ValidatorCoordinate v : new ArrayList<>(keys)) { 
    // ... 
} 

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

+0

Работал как очарование! Не могу не почувствовать, что это maaaay будет обходным путем, позволяющим избежать некоторых мер безопасности, но исходя из моих текущих функций, это не вызовет проблем, поэтому я оставлю его на данный момент и реорганизую его, допустив дату. Благодаря! – bwbonanno

0

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

private boolean tryBlock(ValidatorCoordinate source, 
ValidatorCoordinate destination, 
ValidatorCoordinate underAttack, XiangqiColor c){ 

    boolean blocked = !underAttack(underAttack, c); 
    if (!blocked) 
     board.movePiece(source, destination, c); 

    return blocked; 

}

+0

Ах да. Извините, я должен был уточнить контекст немного больше. К сожалению, идея в том, что я проверяю мат в игре. В принципе, если кусок * может * переместиться, чтобы заблокировать чек, то я не буду отмечать игру как завершающую. Чтобы проверить это, я перемещаю кусок и проверяю, находится ли король до сих пор. Делать это таким образом защищает от вещей, таких как перемещение части, чтобы блокировать Короля, только чтобы выявить другую атакующую фигуру. Спасибо за обратную связь! – bwbonanno

 Смежные вопросы

  • Нет связанных вопросов^_^