はじめに
Mapの値型にSeq、Set、Listあたりを取るときにありがちなコード。
キーが無かったらコレクションをnewして・・・という部分を書くのがかったるい。
※scala.mutable.Seqなどは値の追加をすると新しいコレクションが返ってきてしまうのでここではListBufferを使った。
val m = mutable.Map.empty[Int, ListBuffer[String]]
def addKeyValue(k: Int, v: String) = {
if (!m.contains(k)) {
m += (k -> new ListBuffer[String])
}
m(k) += v
}
MultiMapでリファクタリング
こんな時は、scala.collection.mutable.MultiMapを使うとスッキリ書ける。
MultiMapはtraitであり、具象クラスが用意されていないので以下のようにmix-inして使うのが基本となる。
val m = new mutable.HashMap[Int, mutable.Set[String]] with mutable.MultiMap[Int, String]
def addKeyValue(k: Int, v: String) = m.addBinding(k, v)
但し、MultiMapの値はSet型になるので注意が必要だ。以下のように宣言されている。
TreeSetのような順序を保持できるSetを使うことも出来ない。
trait MultiMap[A, B] extends Map[A, Set[B]] {
自作するにしても、MultiMapのソースコードはたかだかこの程度でありそんなに難しいものでもないだろう。
おまけ
なお、最初のコードは以下のようにMap#getOrElseUpdateメソッドを使うことでもう少し簡潔に書くことも出来る。
val m = mutable.Map.empty[String, ListBuffer[String]]
def addKeyValue(k: String, v: String) = {
val l = m.getOrElseUpdate(k, new mutable.ListBuffer[String])
l += v
}
Java編
あと、Javaでは標準ライブラリには存在しないものの、Google製guavaライブラリには同名のMultiMapが存在する。
同様にif文が不要になり重宝する。
Multimap<Int, String> m = ArrayListMultimap.create();
m.put(1, "one");