Гуава Multimap и concurrentModificationException [дублировать
-
21-09-2019 - |
Вопрос
Этот вопрос уже имеет ответ здесь:
Я не понимаю, почему я получаю 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();
}
}