Как да итерирате HashMap, като същевременно избягвате ConcurrentModificationException

Имам HashMap, той е от типа HashMap<String,HashMap<String,int>> сега трябва да повторя този HashMap и да изтрия вътрешните HashMap, които имат стойност 0 за всеки ключ.

Ако такова премахване направи вътрешната HashMap празна, тогава съответният ключ на вътрешната HashMap се премахва от външната HashMap. Опитах да повторя над него и след това да премахна елементи, които отговарят на изискванията, но това ми хвърля ConcurrentModificationException.

Опитах следния код:

synchronized(MyConstants.cliListUpdateList)
{
    synchronized(MyConstants.cliList)
    {
        outerEntries = MyConstants.cliListUpdateList.entrySet();
        outerIterator = outerEntries.iterator();

        while(outerIterator.hasNext())
        {
            outerEnt = (Entry) outerIterator.next();
            innerHashMap = (HashMap) outerEnt.getValue();
            synchronized(innerHashMap)
            {//synchronize innerhashmap
            innerEntries = innerHashMap.entrySet();
            innerIterator = innerEntries.iterator();
            synchronized(innerIterator)
            {
            while(innerIterator.hasNext())
            {
                innerEnt = (Entry) innerIterator.next();
                int k = Integer.parseInt((String)innerEnt.getValue());
                if(k==0)
                {
                    innerHashMap.remove(innerEnt.getKey());
                    if(innerHashMap.isEmpty())
                    {
                        MyConstants.cliListUpdateList.remove(outerEnt.getKey());
                    }

                    ArrayList ports = (ArrayList) MyConstants.cliList.get(outerEnt.getKey());
                    ports.remove((String)innerEnt.getKey());
                    if(ports.isEmpty())
                    {
                        MyConstants.cliList.remove(outerEnt.getKey());
                    }
                }
                else
                {
                    k--;
                    innerHashMap.put(innerEnt.getKey(), k+"");
                    MyConstants.cliListUpdateList.put(outerEnt.getKey(), innerHashMap);
                }

            }
            }
        }//synchronize innerhashmap
        }


        System.out.println(MyConstants.cliListUpdateList + " <---> "+ MyConstants.cliList);

    }
}

Получавам изключението на този ред: innerEnt = (Entry) innerIterator.next();. Опитах метода за премахване, предоставен от класа Iterator. Но това също не е добре.

РЕДАКТИРАНЕ

от документите на Java знам толкова if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this(ConcurrentModificationException) exception, но имам нужда от точно същата функционалност.


person sasidhar    schedule 12.03.2012    source източник
comment
Опитах колкото се може повече синхронизирани блокове, но не успях :(   -  person sasidhar    schedule 12.03.2012
comment
Синхронизирането няма да ви спаси, защото всичко това се причинява от една нишка. Синхронизирането се използва за сериализиране на операции, извършвани от множество нишки срещу един контейнер, който не е безопасен за нишки, като HashMap.   -  person David Harkness    schedule 12.03.2012


Отговори (2)


Може да не реши проблема ви напълно, но вместо innerHashMap.remove(innerEnt.getKey()); трябва да използвате метода за премахване на итератора innerIterator.remove();

person OldCurmudgeon    schedule 12.03.2012
comment
Точно така, итераторът е проектиран да позволява премахването на текущата двойка ключ/стойност, като същевременно поддържа итератора в такт. В документите за entrySet изрично се казва, че премахването чрез remove е приемливо. Сигурни ли сте, че сте променили всички премахвания, за да използвате подходящия итератор? - person David Harkness; 12.03.2012
comment
В допълнение към горното, използвайте Map.Entry.setValue вместо put, за да намалите брояча във вътрешните карти. Документите предполагат, че използването на put трябва да е добре, но опитайте това. - person David Harkness; 12.03.2012
comment
@DavidHarkness лошо, лошо. Прегледах метод HashMap.remove. Всичко е наред и работи сега. Благодаря ти. Благодаря и за съвета. - person sasidhar; 12.03.2012

Опитахте ли да използвате Synchronized Hashmap? Collections.synchronizedMap(new HashMap()) или погледнете ConcurrentHashMap

person Michael Laffargue    schedule 12.03.2012
comment
от документите на Java знам толкова if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this(ConcurrentModificationException) exception, но имам нужда от точно същата функционалност :( - person sasidhar; 12.03.2012