davidxxx的回答是正确的(+1),指出了地图上的视图集合与地图相链接,而在迭代视图集合时修改地图可能会导致
ConcurrentModificationException
。地图上的视图集合由
entrySet
、
keySet
和
values
方法提供。因此,原始代码:
Map<Key, Value> map = getMap();
for (Key key : map.keySet()) {
if (isToRemove(key)) {
map.remove(key);
} else {
map.add(key, getNewValue());
}
}
在遍历过程中修改Map,很可能会抛出ConcurrentModificationException
异常。
如果遍历的集合支持remove
操作,那么可以在迭代视图集合时从Map中删除条目。HashMap的视图集合迭代器支持此操作。同样,使用在Map的entrySet
上进行迭代时获取到的Map.Entry
实例的setValue
方法,可以设置特定映射条目(键值对)的值。因此,在单次迭代内即可完成所需操作,无需使用临时Map。以下是实现代码:
Map<Key, Value> map = getMap();
for (var entryIterator = map.entrySet().iterator(); entryIterator.hasNext(); ) {
var entry = entryIterator.next();
if (isToRemove(entry.getKey())) {
entryIterator.remove();
} else {
entry.setValue(getNewValue());
}
}
注意Java 10中使用了
var
关键字。如果你没有使用Java 10,你需要明确地写出类型声明:
Map<Key, Value> map = getMap();
for (Iterator<Map.Entry<Key, Value>> entryIterator = map.entrySet().iterator(); entryIterator.hasNext(); ) {
Map.Entry<Key, Value> entry = entryIterator.next();
if (isToRemove(entry.getKey())) {
entryIterator.remove();
} else {
entry.setValue(getNewValue());
}
}
最后,考虑到这是一个相当复杂的映射操作,使用流来完成工作可能会很有成效。请注意,这将创建一个新的映射,而不是在原地修改现有的映射。
import java.util.Map.Entry;
import static java.util.Map.entry; // requires Java 9
Map<Key, Value> result =
getMap().entrySet().stream()
.filter(e -> ! isToRemove(e.getKey()))
.map(e -> entry(e.getKey(), getNewValue()))
.collect(toMap(Entry::getKey, Entry::getValue));