Какой код требуется для непрерывного цикла через HashMap? - PullRequest
1 голос
/ 12 октября 2011

В настоящее время мой код вызывает периодически возникающие ошибки ConcurrentModificationException, возможно, из-за того, как я перебираю HashMap:

for (Map.Entry<String, Entity> entry : entities.entrySet()) {
    String key = entry.getKey();
    Entity item = entry.getValue();
    if (item.isDestroyed()){
        entities.remove(key);
        ViewManager.getInstance().removeItem(key);
        //INSTRUCT THE ENTITY TO PERFORM IT'S DESTROYED BEHAVIOR item.Destroyed()                    
    } else {
        item.update(1);
        ConsoleItem ci = new ConsoleItemImpl(item.getIdentifier(), item.getLocation(), ColorStringConverter.getInstance().StringToColor(item.getSide()), item.getAngle(), item.getShape(), item.toString(), item.isDestroyed(), item.isDamaged());
        ViewManager.getInstance().updateItem(ci);                    
    }

    item.update(1);
}
// updateInfo call
ViewManager.getInstance().updateInfo(summary());
}

Как выполнить непрерывный цикл через HashMap и избежать ошибки ConcurrentModificationException?

Ответы [ 5 ]

5 голосов
/ 12 октября 2011

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

Другой вариант - использовать итератор .

Я переписал ваш цикл for с итератором ниже:

for(Iterator<Map.Entry<String, Entity>> iterator = entities.entrySet().iterator(); iterator.hasNext(); ){
    Map.Entry<String, Entity> entry = iterator.next();
    String key = entry.getKey();
    Entity item = entry.getValue();
    if (item.isDestroyed()){
        //Notice using an iterator to remove 
        iterator.remove();
        ViewManager.getInstance().removeItem(key);
        //INSTRUCT THE ENTITY TO PERFORM IT'S DESTROYED BEHAVIOR item.Destroyed()                    
    } else {
        item.update(1);
        ConsoleItem ci = new ConsoleItemImpl(item.getIdentifier(), item.getLocation(), ColorStringConverter.getInstance().StringToColor(item.getSide()), item.getAngle(), item.getShape(), item.toString(), item.isDestroyed(), item.isDamaged());
        ViewManager.getInstance().updateItem(ci);                    
    }

    item.update(1);

}

Я не уверен в части непрерывного зацикливания HashMap. Хэш-карта имеет конечный набор ключей, поэтому вы обычно просматриваете набор ключей. Если по какой-то причине вы хотите выполнять непрерывный цикл, вам нужно сначала сообщить нам причину этого, а также терминальное состояние этого непрерывного цикла (это не может быть навсегда , не так ли?)

3 голосов
/ 12 октября 2011

Как задокументировано в других ответах, использование цикла for на основе итератора вместо цикла for-each - ваш лучший выбор для избежания ConcurrentModificationExemptions.Что касается бесконечного цикла, взгляните на метод цикла в статическом служебном классе Guava Iterators.Он принимает Iterable (например, HashMap) и возвращает Iterator, который непрерывно проходит по данным до тех пор, пока Iterable не станет пустым или пока вы не разорвете цикл.

1 голос
/ 12 октября 2011

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

Map<String, String> map = new HashMap<String, String>();
map.put("one", "1");
map.put("two", "2");
map.put("three", "3");

for (Iterator<Map.Entry<String, String>> i = map.entrySet().iterator(); i.hasNext();)
{
    Map.Entry<String, String> curEntry = i.next();
    if (curEntry.getKey().equals("two"))
        i.remove();
}
1 голос
/ 12 октября 2011

как насчет использования итератора и цикла while?

Iterator<String> iterator = map.iterator();
String key;
String value;
while (iterator.hasNext()) {
 key = iterator.next();
 value = map.get(key);
}
0 голосов
/ 12 октября 2011

Предполагая, что вы обращаетесь к карте только в потоке, используйте итератор, как советуют другие парни.

Я думал о производительности, когда перебирал хеш-карту, поэтому я сделал быстрый тест из 25 итераций на 10 ^ 6 случайных длинных и удалил 10% от этого, используя разные реализации карт: ConcurrentHashMap, HashMap, LinkedHashMap и TreeMap.

Предполагается, что связанная хэш-карта предназначена для итерации и, по-видимому, является наиболее эффективной. Я полагаю, шипы из-за gc.

... но различия меньше, чем я ожидал.

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...