If you don't really need immutability, then as others have said, MultiMap
is the way to go. If you do really need immutability, then the approach you've taken is as easy as anything else; there isn't anything built in (AFAIK), and any creation of a immutable MultiMap is going to take a lot more work than the method you've got there.
Whether the representation is superior depends on your usage. Do you often want to do things with all values corresponding to one key? Can you insert the same value multiple times into your map? If yes to both, your representation is the right one.
If you want the same value inserted at most once at one key, then you should use Set[U]
instead of Iterable[U]
(which can easily be done by adding .toSet
to it.map(_._2)
).
If you dislike having to deal with sets/iterables and are just putting up with it (i.e. you'd really rather just have key-value pairs than key-setofvalues pairs), you'd have to write a wrapper class around the map that presents a single map interface and would do the right thing with +, -, and iterator.
Here's an example that turned out a little longer than I had anticipated (here formatted for cut and paste into the REPL):
import scala.collection._
class MapSet[A,B](
val sets: Map[A,Set[B]] = Map[A,Set[B]]()
) extends Map[A,B] with MapLike[A,B,MapSet[A,B]] {
def get(key: A) = sets.getOrElse(key,Set[B]()).headOption
def iterator = new Iterator[(A,B)] {
private val seti = sets.iterator
private var thiskey:Option[A] = None
private var singles:Iterator[B] = Nil.iterator
private def readyNext {
while (seti.hasNext && !singles.hasNext) {
val kv = seti.next
thiskey = Some(kv._1)
singles = kv._2.iterator
}
}
def hasNext = {
if (singles.hasNext) true
else {
readyNext
singles.hasNext
}
}
def next = {
if (singles.hasNext) (thiskey.get , singles.next)
else {
readyNext
(thiskey.get , singles.next)
}
}
}
def +[B1 >: B](kv: (A,B1)):MapSet[A,B] = {
val value:B = kv._2.asInstanceOf[B]
new MapSet( sets + ((kv._1 , sets.getOrElse(kv._1,Set[B]()) + value)) )
}
def -(key: A):MapSet[A,B] = new MapSet( sets - key )
def -(kv: (A,B)):MapSet[A,B] = {
val got = sets.get(kv._1)
if (got.isEmpty || !got.get.contains(kv._2)) this
else new MapSet( sets + ((kv._1 , got.get - kv._2)) )
}
override def empty = new MapSet( Map[A,Set[B]]() )
}
and we can see that this works as desired like so:
scala> new MapSet() ++ List(1->"Hi",2->"there",1->"Hello",3->"Bye")
res0: scala.collection.Map[Int,java.lang.String] = Map(1 -> Hi, 1 -> Hello, 2 -> there, 3 -> Bye)
scala> res0 + (2->"ya")
res1: scala.collection.Map[Int,java.lang.String] = Map(1 -> Hi, 1 -> Hello, 2 -> there, 2 -> ya, 3 -> Bye)
scala> res1 - 1
res2: scala.collection.Map[Int,java.lang.String] = Map(2 -> there, 2 -> ya, 3 -> Bye)
(though if you wanted to get a MapSet back after ++, you'd need to override ++; the Map hierarchy doesn't have its own builders to take care of things like this).