Javaコードを書いていて、for each 内で、Mapのremoveを行いjava.util.ConcurrentModificationExceptionエラーになるのをすっかり忘れてました。
エラーになるコード
HashMap<String, String> map = new HashMap<String, String>();
map.put("k1", "v1");
map.put("k2", "v2");
map.put("k3", "v3");
for(String k : map.keySet()){
if( k.endsWith("2")){
map.remove(k);
}
}
エラーにならないコード
HashMap<String, String> map = new HashMap<String, String>();
map.put("k1", "v1");
map.put("k2", "v2");
map.put("k3", "v3");
for(Iterator<String> i = map.keySet().iterator();i.hasNext();){
String k = i.next();
if( k.endsWith("2")){
i.remove();
}
}
Iteratorで要素を順に取得する場合は、要素の削除は Iterator.remove で行う必要がありました。(拡張for文はコンパイル時にIteraterのコードに展開されるようです)
すっかり拡張for文に慣れてしまいIteratorなんてほぼ目にしなくなっているのに、removeするときだけIteratorで回すのは面倒くさいなーと思いました。
removeではなく、新しいMapを作成する
処理に問題がなければ、removeした結果と同じMapを新規に作成しとけば問題ないですよね。
HashMap<String, String> map = new HashMap<String, String>();
map.put("k1", "v1");
map.put("k2", "v2");
map.put("k3", "v3");
HashMap<String, String> newmap = new HashMap<String, String>();
for(String k : map.keySet()){
if( k.endsWith("2") == false){
newmap.put(k, map.get(k));
}
}
map = newmap;
さらにStreamAPIで綺麗に書けるようになってます。
HashMap<String, String> map = new HashMap<String, String>();
map.put("k1", "v1");
map.put("k2", "v2");
map.put("k3", "v3");
map = map.entrySet().stream()
.filter(m-> m.getKey().endsWith("2")==false)
.collect(
Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e1,HashMap::new)
);
でもラムダ式に慣れてなくて、いちいち調べないとまともに書けないですねー。ちゃんと把握しようかな。。と思いました。