Mr Chasi
Mr Chasi

Reputation: 437

Iterating a Map - why are operations undefined?

Regarding Map.Entry, the Java documentation states:

A map entry (key-value pair). The Map.entrySet method returns a collection-view of the map, whose elements are of this class. The only way to obtain a reference to a map entry is from the iterator of this collection-view. These Map.Entry objects are valid only for the duration of the iteration; more formally, the behavior of a map entry is undefined if the backing map has been modified after the entry was returned by the iterator, except through the setValue operation on the map entry.

This suggests that iterating over a Map is read-only. If operations on Map objects (such as put() etc.) are undefined, how does one bulk-update entries in a Map?

Upvotes: 0

Views: 87

Answers (1)

Thomas Kläger
Thomas Kläger

Reputation: 21490

The code

Map<String, String> myMap = new HashMap<>(Map.of("a", "b"));
for (Map.Entry<String, String> entry: myMap.entrySet()) {
    entry.put("foo", "foo");
}

doesn't compile because entry is a Map.Entry and Map.Entry doesn't have a put() method.


The fragment

Map<String, String> myMap = new HashMap<>(Map.of("a", "b"));
for (Map.Entry<String, String> curEntry: myMap.entrySet()) {
    myMap.put("foo", "foo");
    // the behavior of curEntry is undefined after the preceding line
}

is the case the Map.Entry documentation mentions: myMap is modified while iterating over it. That means that according to the documentation the behavior of curEntry is undefined after the modifying line.

Please note that modifying the map while iterating over it also leads to unspecified behavior for the iterator:

If the map is modified while an iteration over the set is in progress (except through the iterator's own remove operation, or through the setValue operation on a map entry returned by the iterator) the results of the iteration are undefined.


The fragment

Map<String, String> myMap = new HashMap<>(Map.of("a", "b"));
Map<String, String> otherMap = new HashMap<>(Map.of("c", "d"));
for (Map.Entry<String, String> entry: myMap.entrySet()) {
    otherMap.put("foo", "foo");
}

is OK because this code modifies otherMap while iterating over myMap. Since these are two different Maps this is allowed.


Bulk-Updating a Map while iterating over it:

Map<String, Integer> myMap = new HashMap<>(Map.of("a", 1));
for (Map.Entry<String, Integer> entry: myMap.entrySet()) {
    entry.setValue(m.getValue() + 1);
}

is OK, because this code uses the supported Map.Entry.setValue() method to update the value of an entry.

Upvotes: 2

Related Questions