Любая идея, почему я не получаю java .util.ConcurrentModificationException при удалении ключа из HashMap? - PullRequest
0 голосов
/ 07 февраля 2020

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

Мой фрагмент кода:

public class hello
{
    private static HashMap<String,String> fileMap;
    private static final String KEY_REMOVED = "d";
    static {
        fileMap = new HashMap<String, String>();
        fileMap.put("a", "hello");
        fileMap.put(KEY_REMOVED, "bye");
        fileMap.put("c", "hi");
    }

    public static void main(String []args){
        upload();
}

    private static void upload() throws  ConcurrentModificationException {

        for (Map.Entry<String, String> entry : fileMap.entrySet()) {
            System.out.println("Uploading key " + entry.getKey());

            String fileName = entry.getKey();

            if (fileName.equals(KEY_REMOVED)) {
                fileName = new Timestamp(System.currentTimeMillis()).getTime();
                String temp2 = fileMap.remove(VIDEO_MP4_ASSET);
                System.out.println("hashmap after removing key is " + fileMap);
                System.out.println("adding  key to hashmap " + fileName);
                fileMap.put(fileName, temp2);

            } else {

                System.out.println("continue");
            }
            System.out.println("hashmap is " + fileMap);
        }
    }

В идеале при удалении ключа мы должны получить ConcurrentModificationException. Я беспокоюсь, что я не получаю его прямо сейчас. Но мой код может сломаться позже. Это так?

Ответы [ 4 ]

2 голосов
/ 07 февраля 2020

Это может немного сбивать с толку, так как Javado c сначала заявляет, что

Итераторы, возвращаемые всеми "методами представления коллекции" этого класса (values(), keySet(), entrySet()) не подвержены сбоям: если карта в любой момент после создания итератора структурно изменяется, любым способом, кроме как через собственный метод удаления итератора, итератор выдаст ConcurrentModificationException.

Примечание. что отказоустойчивое поведение итератора не может быть гарантировано

Но оно также заявляет в HashMap.entrySet ()

Возвращает представление Set из отображений, содержащихся в этой карте. Набор опирается на карту, поэтому изменения в карте отражаются в наборе, и наоборот. Если карта изменяется во время выполнения итерации по набору (кроме как через собственную операцию удаления итератора или через операцию setValue для записи карты, возвращаемой итератором) результаты итерации не определены .

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

Так что, если вы выполняете итерации keySet(), values() или entrySet() и Вы модифицируете Map структурно, то есть remove() или put новым ключом (не заменяя значение существующего ключа), вы можете получить: ConcurrentModificationException или странные вещи, происходящие с итерацией, например как пропуск элементов или столкновение с элементом дважды.

0 голосов
/ 07 февраля 2020

Является ли это правильным способом переименования ключа в hashmap при итерации?

Сохранение значения в переменной temp, удаление ключа и добавление новой записи с новым ключом и temp

Iterator<Map.Entry<String,String>> iter = fileMap.entrySet().iterator();
while (iter.hasNext()) { 

Map.Entry<String, String> entry = iter.next(); 
fileName = entry.getKey(); 

if (fileName.equals(KEY_REMOVED)) {
fileName = new Timestamp(System.currentTimeMillis()).getTime(); 
String temp = entry.getValue(); 
iter.remove(); 
System.out.println("hashmap after removing key is " + fileMap); 
fileMap.put(fileName, temp); 
System.out.println("Updated hashmap after addng key is " + fileMap); 
} 
else
{
 System.out.println("continue");
}
}
0 голосов
/ 07 февраля 2020

ConcurrentModificationException не предназначен для того, чтобы быть безопасным средством узнать, что-то меняет вашу коллекцию, поскольку в документации по HashMap говорится: «Обратите внимание, что отказоустойчивое поведение итератора не может быть гарантировано, поскольку вообще невозможно сделать что-либо жесткие гарантии при наличии несинхронизированных одновременных модификаций. Отказоустойчивые итераторы генерируют исключение ConcurrentModificationException на основе максимальных усилий. Поэтому было бы неправильно писать программу, которая зависела от этого исключения в отношении ее корректности: поведение итераторов при быстром отказе должно использовать только для обнаружения ошибок "

, если вы хотите быть уверены, что никто не изменяет вашу коллекцию, вы можете использовать синхронизированную версию HashMap:

Map m = Collections.synchronizedMap (new HashMap (.. .));

0 голосов
/ 07 февраля 2020

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

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

    for (Map.Entry<String, String> entry : fileMap.entrySet()) {

на

    for (Map.Entry<String, String> entry : new ArrayList<>(fileMap.entrySet())) {
...