الجوافة multimap و ConviSdificationException [مكررة
-
21-09-2019 - |
سؤال
هذا السؤال لديه بالفعل إجابة هنا:
لا أفهم لماذا أحصل على concurrentModificationException عندما أتكرر من خلال هذا 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 ، يمكنك أيضًا استخدام نهج Lambda:
eventMultiMap.entries().removeIf(genericEventCommandEntry -> genericEventCommandEntry.getValue().equals(command));
نصائح أخرى
إن استدعاء إزالة على مجموعة أثناء التكرار من خلالها سيؤدي إلى حدوث متزامن في كل مرة ، حتى لو تم كل شيء في نفس الخيط - الشيء الصحيح الذي يجب القيام به هو الحصول على تكرار صريح واتصال .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
عندما تحاول الوصول إلى المفتاح التالي - لأن إفراغ مجموعة يؤدي إلى إزالة المفتاح ، وبالتالي تعديل مفاتيح Multimap.
إذا تمكن مؤشر ترابط آخر من تعديل Multimap الخاص بك أثناء تشغيل هذا المنطق ، فستحتاج إلى إضافة كتلة متزامنة إلى رمز 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 () التزامن. ومع ذلك ، إذا قمت بحذف الكتلة المتزامنة ، فيمكن أن تتحول Multimap بين مكالمة removeall () وواحدة من مكالمات Size () ، مما يؤدي إلى قيمة غير صحيحة لـ NBREMOVED.
الآن ، إذا كان الكود الخاص بك متسلسلًا واحدًا ، وكنت ترغب فقط في تجنب مكالمة ConcurrentModificationException ، فيمكنك ترك 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();
}
}