Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[Map]modifying a map through an iteration

3 views
Skip to first unread message

Daniel Moyne

unread,
Nov 1, 2008, 7:20:22 PM11/1/08
to
I have built a map like this :

private Map<String,ArrayList<Indi>> allNamePartitionsMap=new HashMap<String,ArrayList<Indi>>();

where basically to a string key I associate in my map a list of objects of type Indi

Now I want to iterate through this map but with the possibility to modify it dynamically ; for this I use this loop where I search for indi0 in map values to remove it from keys (only one key can have such indi0):

for (Map.Entry<String, ArrayList<Indi>> entry : allNamePartitionsMap .entrySet()) {
if (entry.getValue().contains(indi0)) {
/* I remove indi0 from the list of indi */
entry.getValue().remove(indi0);
/* question is this enough to update my map though
* working with an entry object and not directly on my
* map ??????
*/
if (entry.getValue().size() == 0) {
/* I want to remove the key from the map
* how to do it ??????
*/
}
break;
}
}

Can this confuse the position of the iterator over the set though modifications are only supposed to take place upfront

Thanks
--
Daniel Moyne (Nulix) \\|||// Machine : x86_64
Distribution : Ubuntu Feisty Fawn / --- \ ATI Radeon X300
kernel : 2.6.20-16-generic (' o-o ') KDE 3.5.7
--------------------------------oOO-(_)-OOo---------------------------------

John B. Matthews

unread,
Nov 2, 2008, 12:23:19 AM11/2/08
to
In article <490ce436$0$28669$7a62...@news.club-internet.fr>,
Daniel Moyne <daniel...@neuf.fr> wrote:

> I have built a map like this :
>
> private Map<String,ArrayList<Indi>> allNamePartitionsMap=new
> HashMap<String,ArrayList<Indi>>();
>
> where basically to a string key I associate in my map a list of objects of
> type Indi
>
> Now I want to iterate through this map but with the possibility to modify it
> dynamically ; for this I use this loop where I search for indi0 in map values
> to remove it from keys (only one key can have such indi0):
>
> for (Map.Entry<String, ArrayList<Indi>> entry : allNamePartitionsMap
> .entrySet()) {
> if (entry.getValue().contains(indi0)) {
> /* I remove indi0 from the list of indi */
> entry.getValue().remove(indi0);

> /* question is this enough to update my map though working
> * with an entry object and not directly on my map?
> */

I don't see a problem, as suggested in the example below. The (hidden)
iterator is unaffected; only the object referenced in the key is
modified.

> if (entry.getValue().size() == 0) {
> /* I want to remove the key from the map
> * how to do it?

> */
> }
> break;
> }
> }
>
> Can this confuse the position of the iterator over the set though

> modifications are only supposed to take place upfront?

Almost certainly. Instead, mark the value for maintenance, and remove()
the value from the map outside the loop (not shown). I don't know how to
remove the key, but I guess you could rebuild the Map, excluding the
key, if you had to. Alternatively, consider a WeakHashMap.

<http://java.sun.com/javase/6/docs/api/java/util/WeakHashMap.html>

<code>
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* @author John B. Matthews
*/
public class MapTest {

public static final Map<String, List<String>> map =
new HashMap<String, List<String>>();

public static void main(String[] args) {
List<String>list = new ArrayList<String>(
Arrays.asList("Alpha", "Beta", "Gamma"));
map.put("Ordinals", list);

list = new ArrayList<String>(
Arrays.asList("Aleph", "Beth", "Gimel"));
map.put("Cardinals", list);

list = new ArrayList<String>(
Arrays.asList("Alpher", "Bethe", "Gammow"));
map.put("Physicists", list);

printMap(map);
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
if (entry.getValue().contains("Gamma")) {
entry.getValue().remove("Gamma");
}
}
printMap(map);
}

private static void printMap(Map<String, List<String>> map) {
for (Map.Entry entry : map.entrySet()) {
System.out.println(entry.getKey()
+ " " + entry.getValue());
}
}
}
</code>

--
John B. Matthews
trashgod at gmail dot com
http://home.roadrunner.com/~jbmatthews/

Roedy Green

unread,
Nov 2, 2008, 3:50:12 AM11/2/08
to
On Sun, 02 Nov 2008 00:20:22 +0100, Daniel Moyne
<daniel...@neuf.fr> wrote, quoted or indirectly quoted someone who
said :

>}
>
>Can this confuse the position of the iterator over the set though modifications are only supposed to take place upfront

The whole point of iterator.remove is to let you examine and delete
elements without missing any or processing some twice.
--
Roedy Green Canadian Mind Products
http://mindprod.com
A vote for McCain is fearful clinging to McSame.
A vote for Obama is a shot at Obamalot.

Daniel Moyne

unread,
Nov 2, 2008, 6:53:22 AM11/2/08
to
John B. Matthews wrote:

John,
can we say :
(1) that within the loop modifying values attached to a key is not a problem because the iteration is done via the key, this on keys already processed or to be processed,
(2) that modifications applied to entries are equivalent to modifications applied to the map (within the loop) meaning that if for some reasons in that very loop I have to re-loop through my map I am handling here an updated object ?),
(3) that removing a key may be problematic except the currently processed one with something like :
allNamePartitions.remove(entry.getKey();
as entry.remove does not exist ; this of course if there is full coherence between the map and the generated entry set (?),
(4) that otherwise removing a key backwards or forwards (using for example another nested loop) may not be possible.

Of course as suggested when removing a key I can break the process and rebuild the map by re-entring the loop with something like this if possible (is it possible to remove a key in suche a loop ?) :

toBeReprocessed=true;
while (toBeReprocessed) {
toBeReprocessed=false;
/* looping through the map */
for () {
/* somethingTrue derives here from something
* verified on an entry.getValue()
*/
if (somethingTrue) {
/* re-looping through the map to delete a particular key anywhere */
for () {
/* deleting a particular key but here the one processed */
allNamePartitions.remove(entry.getKey();
/* will the above work ? */
break;
}
toBeReprocessed=true;
break;
}
}
}

As far as keys are concerned this map process could be applied to a list or items when through iteration you want to remove an item other than the one currently processed.

But this if workable is particularly unefficient as I have to reprocess keys again and again each time until I reach the point where I had the last break.

Regards

John B. Matthews

unread,
Nov 2, 2008, 8:39:03 AM11/2/08
to
In article <490d94b2$0$28669$7a62...@news.club-internet.fr>,
Daniel Moyne <daniel...@neuf.fr> wrote:

> [...] Can we say?


>
> (1) that within the loop modifying values attached to a key is not a problem
> because the iteration is done via the key, this on keys already processed or
> to be processed,

Generally, this is correct, but not because of when the key is returned
by the (hidden) iterator. It's because the List returned by
entry.getValue() is being modified, and the Map itself is unchanged.

> (2) that modifications applied to entries are equivalent to modifications
> applied to the map (within the loop) meaning that if for some reasons in that
> very loop I have to re-loop through my map I am handling here an updated
> object ?),

Cephalgia exception.

> (3) that removing a key may be problematic except the currently processed one
> with something like :
> allNamePartitions.remove(entry.getKey();
> as entry.remove does not exist ; this of course if there is full coherence
> between the map and the generated entry set (?),

If you try to remove a value from a Map while iterating you'll get
ConcurrentModificationException (note that for-each hides the iterator,
but it's still there):

<http://java.sun.com/javase/6/docs/api/java/util/HashMap.html>

> (4) that otherwise removing a key backwards or forwards (using for example
> another nested loop) may not be possible.
>
> Of course as suggested when removing a key I can break the process and
> rebuild the map by re-entring the loop with something like this if possible
> (is it possible to remove a key in suche a loop ?) :

[...]


> As far as keys are concerned this map process could be applied to a list or
> items when through iteration you want to remove an item other than the one
> currently processed.
>
> But this if workable is particularly unefficient as I have to reprocess keys
> again and again each time until I reach the point where I had the last break.

Instead of trying to remove Map values while iterating, just remember
which one(s) are slated for annihilation:

<code>
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* @author John B. Matthews
*/
public class MapTest {

private static final Map<String, List<String>> map =


new HashMap<String, List<String>>();

public static void main(String[] args) {
List<String> list = new ArrayList<String>(
Arrays.asList("Alpha", "Beta", "Gamma"));
map.put("Ordinals", list);

list = new ArrayList<String>(
Arrays.asList("Aleph", "Beth", "Gimel"));
map.put("Cardinals", list);

list = new ArrayList<String>(
Arrays.asList("Alpher", "Bethe", "Gammow"));
map.put("Physicists", list);

List<String> mapValuesToRemove = new ArrayList<String>();


printMap(map);
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
if (entry.getValue().contains("Gamma")) {

entry.getValue().remove("Gamma"); // OK
// throws ConcurrentModificationException
// map.remove(entry.getKey());
// if it's time to go?
mapValuesToRemove.add(entry.getKey());
}
}
for (String s : mapValuesToRemove) map.remove(s);
printMap(map);
}

private static void printMap(Map<String, List<String>> map) {
for (Map.Entry entry : map.entrySet()) {
System.out.println(entry.getKey()
+ " " + entry.getValue());
}
}
}
</code>

--

Lew

unread,
Nov 2, 2008, 8:57:41 AM11/2/08
to

or use iterator.remove(), if the iterator supports it.

--
Lew

John B. Matthews

unread,
Nov 2, 2008, 9:05:43 AM11/2/08
to
In article <0dqqg4libsbftoeq7...@4ax.com>,
Roedy Green <see_w...@mindprod.com.invalid> wrote:

> On Sun, 02 Nov 2008 00:20:22 +0100, Daniel Moyne
> <daniel...@neuf.fr> wrote, quoted or indirectly quoted someone who
> said :
>
> >Can this confuse the position of the iterator over the set though
> >modifications are only supposed to take place upfront
>
> The whole point of iterator.remove is to let you examine and delete
> elements without missing any or processing some twice.

Good point. Although the for-each loop hides the iterator, an explicit
iterator can remove elements from the Map during iteration:

<code>
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
* @author John B. Matthews
*/
public class MapTest {

private static final Map<String, List<String>> map =
new HashMap<String, List<String>>();

public static void main(String[] args) {
List<String> list = new ArrayList<String>(
Arrays.asList("Alpha", "Beta", "Gamma"));
map.put("Ordinals", list);

list = new ArrayList<String>(
Arrays.asList("Aleph", "Beth", "Gimel"));
map.put("Cardinals", list);

list = new ArrayList<String>(
Arrays.asList("Alpher", "Bethe", "Gammow"));
map.put("Physicists", list);

printMap(map);
Iterator<Entry<String, List<String>>> iter =
map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, List<String>> entry = iter.next();


if (entry.getValue().contains("Gamma")) {
entry.getValue().remove("Gamma");

iter.remove();

John B. Matthews

unread,
Nov 2, 2008, 9:43:21 AM11/2/08
to
In article <gekbkl$2n5$1...@aioe.org>, Lew <no...@lewscanon.com> wrote:

> John B. Matthews wrote:
[...]


> > Instead of trying to remove Map values while iterating, just remember
> > which one(s) are slated for annihilation:
>
> or use iterator.remove()

Lew: Quite right. Although it requires an explicit iterator, this is
surely closer to the OP's intent:

<http://groups.google.com/group/comp.lang.java.help/msg/af8ff8177bd0837e?
hl=en>

[...] if the iterator supports it.

Daniel M: the API caveat still applies, "...if the map is structurally
modified at any time after the iterator is created, in any way except
through the iterator's own remove or add methods, the iterator will
throw a ConcurrentModificationException." The Last paragraph of this
page expands on the topic:

<http://java.sun.com/j2se/1.5.0/docs/guide/language/foreach.html>

Daniel Moyne

unread,
Nov 2, 2008, 5:57:48 PM11/2/08
to
John B. Matthews wrote:

Thanks to John and all other that have participated :

In my case I do not have really to to delete keys in the main loop ; this was intended to simplify the cleaning process as when I examine a key item in some cases I have to remove indi's from a list of a particular key ahead of my main loop iterator (key found in the nested loop though another ieration) ; as in some cases I will empty the list of indi's of a particular key I thought of removing it to speed up the process both in the main loop and in the nested one where I clean lists of indi's ahead (only ahead as as particulatity of my data) but my algorithm still works all right (not yet tested with java) ; though as cleaning operations take place only ahead of my iterator of main loop I wish I could memorise the iterator to impose a start at this position in the nested loop but here as we do not handle index as in arrays but an object iterator is looks like it is not possible (?).

To summarize :
- operating on entry.getValue() do modify the map itself (ie : modifying a list of indi's in whichever key (that could be a processed key or a key not yet processed of the main loop) in the nested loop is like doing the same thing in the main loop and vice versa) ; this does not affect the iterator of the main loop neither the order of the keys in the map.
- deleting keys through an iteration can only be done on currently processed key with autorized methods like iterator.remove() (I wondered wether map.remove(entry.getKey()) would not be equivalent if an explicit iterator is not used but as in the loop we switch back from entrySet back to map it could not be known to the iterator) otherwise the key structure will be definitely messed up.


Regards.

0 new messages