Вопрос

Я не понимаю, почему я получаю condurentModificationException, когда я перетекаю через это multimapАнкет Я прочитал следующее вход, но я не уверен, понял ли я все это. Я попытался добавить синхронизированный блок. Но я сомневаюсь, что синхронизировать и когда.

А multimap это поле и создано как это:

private Multimap<GenericEvent, Command> eventMultiMap =   
   Multimaps.synchronizedMultimap(HashMultimap.<GenericEvent, Command> create());

и использовался как это:

eventMultiMap.put(event, command);

И вот так (я пытался синхронизировать эту часть на карте, но без успеха)

for (Entry<GenericEvent, Command> entry : eventMultiMap.entries()) {
    if (entry.getValue().equals(command)) {
        eventMultiMap.remove(entry.getKey(), entry.getValue());
        nbRemoved++;
    }
}
Это было полезно?

Решение

В Java8 вы также можете использовать лямбда -подход:

eventMultiMap.entries().removeIf(genericEventCommandEntry -> genericEventCommandEntry.getValue().equals(command));

Другие советы

Призыв к удалению на коллекции, пока вы итерация через нее приведет к тому, что CondurentModificationException каждый раз, даже если все это сделано в одном и том же потоке - правильное дело - это получить явный итератор и вызовать .Remove () на этом.

Изменить: изменить ваш пример:

Iterator<Map.Entry<GenericEvent, Command>> i = eventMultiMap.entries().iterator();
while (i.hasNext()) {
    if (i.next().getValue().equals(command)) {
        i.remove();
        nbRemoved++;
    }
}

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

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

synchronized (eventMultimap) {
  Iterator<Entry<GenericEvent, Command>> i = eventMultiMap.entries.iterator();
  while (i.hasNext()) {
    if (i.next().getValue().equals(command)) {
        i.remove();
        nbRemoved++;
    }
  }
}

Или вы можете опустить итератор следующим образом,

synchronized (eventMultimap) {
  int oldSize = eventMultimap.size();
  eventMultimap.values().removeAll(Collections.singleton(command));
  nbRemoved = oldSize - eventMultimap.size();
}

Вызов removeall () не требует синхронизации. Однако, если вы опустите синхронизированный блок, мультимап может мутировать между вызовом removeall () и одним из вызовов Size (), что приводит к неправильному значению Nbremoved.

Теперь, если ваш код является однопоточным, и вы просто хотите избежать вызова condurentModificationException, вы можете оставить логику MultiMaps.SynchronizedMultimap и синхронизированную (EventMultimap).

я предпочитаю Multimap.values().iterator() Если вы не заботитесь о ключе. Вы также должны пытаться держаться подальше от использования синхронизированных блоков как можно больше, потому что вы не можете определить приоритеты чтения/записи эффективно.

ReadWriteLock lock = new ReentrantReadWriteLock();
Lock writeLock = lock.writeLock(); 

public void removeCommands(Command value) {
  try {
    writeLock.lock();
    for (Iterator<Command> it = multiMap.values().iterator(); it.hasNext();) {
      if (it.next() == value) {
        it.remove();
      }
    }
  } finally {
    writeLock.unlock();
  }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top