Nim*_*tar 2 java iteration collections
我知道在迭代期间不应修改集合。因此,我们应该有解决方法。
我有一个代码:
Map<Key, Value> map = getMap(); // map generating is hidden
for (Key key : map.keySet()) {
if (isToRemove(key)) {
map.remove(key);
} else {
map.put(key, getNewValue());
}
}
Run Code Online (Sandbox Code Playgroud)
是未定义的行为还是有效的代码?
keySet文档说,映射的更改反映在返回的set中,反之亦然。这是否意味着以前的代码是不可接受的?
来自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());
}
}
Run Code Online (Sandbox Code Playgroud)
很可能会抛出,ConcurrentModificationException因为它在每次迭代期间都会修改地图。
如果该视图集合的迭代器支持该remove操作,则可以在迭代视图集合时从映射中删除条目。HashMap的视图集合的迭代器确实支持这一点。这是也可以通过使用设置一个特定的映射条目(键-值对)的值setValuea的方法Map.Entry实例,同时迭代地图的获得entrySet。因此,可以在一次迭代中完成您想做的事情,而无需使用临时映射。这是执行此操作的代码:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
注意使用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());
}
}
Run Code Online (Sandbox Code Playgroud)
最后,由于这是一个相当复杂的地图操作,因此使用流来完成工作可能会富有成果。请注意,这将创建一个新地图,而不是就地修改现有地图。
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));
Run Code Online (Sandbox Code Playgroud)