[java] concurrent modification exception

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Goede morgen,

Ik zit met een lastig probleem bij ons java project. In een klasse van containables heb ik volgende methode om een containable te "termineren". (Een containable is een soort rugzak, handtas, ... alles waar je iets in kan stoppen).

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    @Override
    public void terminate() {
        super.terminate();
        for (Long id : items.keySet()) {
            terminateItemsOfIdentification(id);
        }
    }
    
    
    private void terminateItemsOfIdentification(long identification) throws IllegalArgumentException{
        if(!items.containsKey(identification))
            throw new IllegalArgumentException();
        for (Item item : getItemsOfIdentification(identification)) {
            item.terminate();
        }
    }


De fout gebeurt nadat super.terminate() is opgeroepen (waar niets gevaarlijks gebeurt, juist 2 velden die worden aangepast).
Containables bevatten een HashMap<Long,HashSet<Item>>, een hashmap met als key een identificatieNr en als value een hashset. (Dit komt omdat we moeten werken met semi unieke id's voor Items en een methode die controleert of er een Item met een bepaald id in zit moet in constante tijd werken).
1 de 1e methode wordt er dus gelooped over alle keys, en voor elke key worden alle bijhorende Items getermineerd. De methode getItemsOfIdentification(identification) doet hetzelfde als items.get(identification), het retourneerd de value bij een bepaalde id, een HashSet dus.
Elk Item in getItemsOfIdentification(identification) wordt getermineerd via de methode terminate (niets speciaals, wat wel van belang kan zijn is dat het item van de lijst wordt losgekoppeld).

Alles goed en wel, maar het wil niet werken... Ik krijg een ConcurrentModificationException. Deze exception wil zeggen dat je niet over een lijst kan loopen als een andere methode de lijst aan het aanpassen is.
Ik zie niet echt in waarom ik deze Exception krijg: pas na dat de methode terminateItemsOfIdentification(id) is voltooid, ga je toch pas naar de volgende ID?
Ik heb nog al eens gelooped over een lijsten in een lijst, maar deze Exception meen ik mij daar niet bij te herrineren.

Iemand die een idee heeft waar dat het fout loopt?

Bij voorbaat dank :)

Acties:
  • 0 Henk 'm!

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 21-08 23:06

HMS

ConcurrentModificationExceptions gebeuren (bijna) altijd als je met meerdere threads op hetzelfde object (HashMap) zit te werken.

Probeer de map eens te synchronized, handmatig of via Collections.synchronizedMap(<hashmap hier>);


edit:
Trouwens, verwijdert item.terminate() de item ook uit de lijst? Dan krijg je namelijk ook een concurrentmodification exception. Probeer dit met een Enumerator te doen.

[ Voor 28% gewijzigd door HMS op 13-04-2010 10:10 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ik heb het probleem gevonden.... Alleen geen elegante opl.
In Item.terminate wordt removeItem opgeroepen, deze ziet er zo uit:
Java:
1
2
3
4
5
6
7
8
@Override
    public void removeItem(Item item) throws IllegalArgumentException {
        if (!hasAsItem(item))
            throw new IllegalArgumentException();
        items.get(item.getIdentification()).remove(item);
        if (items.get(item.getIdentification()).size() == 0)
            items.remove(item.getIdentification());
    }


het zit em in de laatste if statement. Het is duidelijk dat daar de concurrentmodificationexception gebeurt en als ik dit deel uitcommentarieer, dan is het opgelost, alleen werkt de methode natuurlijk niet zoals ik wil, ik wil geen lege lijsten overhouden, ik wil geen key-value combo's waarbij de value een lege lijst is.

iemand een elegante oplossingen of een hint in de goede richting? Zoals ik het nu zou doen is gewoon een methode cleanUp maken en deze oproepen na de iteratie over alle keys...

@HMS: je edit klopt idd. Maar een Item verwijderen uit een lijst die een value is gaat wel goed, de lijst zelf verwijderen niet (wat logisch is heb ik nu door).
Ik weet niet wat enumerator's zijn (ken wel enums en iterators, maar die zijn nog wel iets anders wss), dus ik zal er eens naar googlen

[ Voor 13% gewijzigd door Verwijderd op 13-04-2010 10:17 ]


Acties:
  • 0 Henk 'm!

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 21-08 23:06

HMS

Sorry, ik was in de war met C# ;-). Je moet de iterator() gebruiken, die ondersteund namelijk wel het verwijderen van items terwijl je itereert over de loop. Helaas moet je het dan wel door de iterator gooien.

(Meer info: http://java.sun.com/javas...i/java/util/Iterator.html)

Of je moet je tweede loop aanpassen:

Java:
1
2
3
4
5
6
7
private void terminateItemsOfIdentification(long identification) throws IllegalArgumentException{ 
        if(!items.containsKey(identification)) 
            throw new IllegalArgumentException(); 
        for (Item item : getItemsOfIdentification(identification).toArray()) { 
            item.terminate(); 
        } 
    }


Zo bijvoorbeeld wel kunnen werken (note: untested). Dit omdat de lijst / map waarover je itereert dan niet aangepast wordt.

[ Voor 45% gewijzigd door HMS op 13-04-2010 10:28 . Reden: Niet genoeg koffie ]


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
@HMS: bedankt voor je replies

Ik heb nochthans met een iterator gewerkt, toen ik voor het eerst deze exception kreeg heb ik alles even omgezet naar het werken met een iterator.

De toArray approach is ook niet voor mij van toepassing aangezien bij mij de lijst wel moet aangepast worden :)

Ik heb het nu zo gedaan: na de lus in terminate wordt volgende methode opgeroepen:
Java:
1
2
3
4
5
6
private void cleanUp() {
        for (Long id : items.keySet()) {        
            if (items.get(id).size() == 0)
                items.remove(id);
        }
    }


Deze methode stond dus eerst voor een deel in removeItem(item)
echter, hij geeft nog altijd concurrentmodificationException... En dit terwijl die methode pas wordt opgeroepen NADAT de for lus is doorlopen, mijn kennis van Java schiet hier toch echt wel te kort om dat te begrijpen. Is er een logische verklaring?

EDIT:
ga nu eens wat proberen met de remove() functie van een iterator

[ Voor 4% gewijzigd door Verwijderd op 13-04-2010 10:49 ]


Acties:
  • 0 Henk 'm!

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 21-08 23:06

HMS

Verwijderd schreef op dinsdag 13 april 2010 @ 10:46:
EDIT:
ga nu eens wat proberen met de remove() functie van een iterator
Dat zou moeten werken ;). De iterator houdt volgens mij namelijk zelf ook een state bij.

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
HMS schreef op dinsdag 13 april 2010 @ 10:57:
[...]


Dat zou moeten werken ;). De iterator houdt volgens mij namelijk zelf ook een state bij.
Mmz ik krijg het toch niet werkend, ik kan geen remove() doen namelijk op de hashmap zelf, wel vb op de keySet() van de hashMap, maar daar ben ik natuurlijk niets mee...

Het vreemde vind ik dat die cleanUp() dezelfde concurrentModificationException geeft nadat de for lus is gelooped...

EDIT:
Ha, de fout zit volgens mij in de methode cleanUp() zelf! dan kan ik wel met een iterator gaan werken lijkt mij, ok, poging zoveel dan maar :) . Dat is wss wat jij bedoelde HMS ^^

EDIT2:
De methode cleanUp() ziet er nu zo uit:

Java:
1
2
3
4
5
6
7
private void cleanUp() {
Iterator<Long> it = items.keySet().iterator();
while (it.hasNext()) {
    if (items.get(it.next()).size() == 0)
        items.remove(it.next());
    }
}


hij geeft geen concurrentmodificationException meer, maar de id's zitten er nog steeds in.... de methode remove van een hashmap verwijdert toch de key en value combo?

[ Voor 38% gewijzigd door Verwijderd op 13-04-2010 11:15 ]


Acties:
  • 0 Henk 'm!

  • CoolGamer
  • Registratie: Mei 2005
  • Laatst online: 08:27

CoolGamer

What is it? Dragons?

Hij bedoelde waarschijnlijk zoiets:
Java:
1
2
3
4
5
6
7
8
9
10
private void cleanUp() {
  Iterator iterator  = items.keySet().iterator();
  while(iterator.hasNext())
  {
    if (items.get(iterator.next()) == 0)
    {
      iterator.remove();
    }
  }
}

Je mag je verzameling niet aanpassen vanbuiten de iterator, ook niet van de zelfde thread. Dus als je zoiets wilt zal je remove() van de iterator zelf moeten aanroepen.

@Edit: je moet remove aanroepen van de iterator. Tevens, wanneer je een iterator gebruikt zal je slechts 1 maal gebruik moeten maken van it.next() binnen je while-lus, anders ga je weer naar de volgende.

[ Voor 19% gewijzigd door CoolGamer op 13-04-2010 11:17 ]

¸.·´¯`·.¸.·´¯`·.¸><(((º>¸.·´¯`·.¸><(((º>¸.·´¯`·.¸.·´¯`·.¸.·´¯`·.¸<º)))><¸.·´¯`·.¸.·´¯`·.¸.·´¯`·.¸


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
@thecoolgamer: bedankt! het werkt, bedankt ook voor je edit, is nuttige info :)

Dus als je een key uit de keySet van een HashMap verwijdert, verwijdert hij ook die key uit de hashMap zelf. Ik neem aan dat bijhorende value ook wordt verwijdert dan?

[ Voor 10% gewijzigd door Verwijderd op 13-04-2010 11:18 ]


Acties:
  • 0 Henk 'm!

  • CoolGamer
  • Registratie: Mei 2005
  • Laatst online: 08:27

CoolGamer

What is it? Dragons?

Ja, klopt. Staat tevens toegelicht in de documentatie.

¸.·´¯`·.¸.·´¯`·.¸><(((º>¸.·´¯`·.¸><(((º>¸.·´¯`·.¸.·´¯`·.¸.·´¯`·.¸<º)))><¸.·´¯`·.¸.·´¯`·.¸.·´¯`·.¸


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
TheCoolGamer schreef op dinsdag 13 april 2010 @ 11:23:
Ja, klopt. Staat tevens toegelicht in de documentatie.
idd, maar ik was aan het twijfelen omwille van het feit dat er ook een methode remove is voor de hashmap zelf, deze neemt een key als argument. Blijkbaar werkt die methode toch net wat anders.
Pagina: 1