Jeśli naprawdę nie potrzeba niezmienności, a następnie jak mówili inni, MultiMap
jest droga. Jeśli naprawdę potrzebujesz niezmienności, to podejście, które podjąłeś, jest tak łatwe, jak wszystko inne; nie ma nic wbudowanego (AFAIK), a każde stworzenie niezmiennej MultiMapy zajmie znacznie więcej pracy niż metoda, którą tam masz.
To, czy przedstawienie jest lepsze, zależy od sposobu użycia. Czy często chcesz robić rzeczy ze wszystkimi wartościami odpowiadającymi jednemu kluczowi? Czy możesz wstawić tę samą wartość wiele razy do swojej mapy? Jeśli tak dla obu, twoja reprezentacja jest właściwa.
Jeśli chcesz taką samą wartość włożonej co najwyżej raz na jednym kluczu, należy użyć Set[U]
zamiast Iterable[U]
(co można łatwo zrobić, dodając .toSet
do it.map(_._2)
).
Jeśli nie lubisz mieć do czynienia z setami/iterabelami i po prostu się z tym pogodzisz (to znaczy, że wolisz raczej pary klucz-wartość niż pary klucz-wartość_wartości), musisz napisać klasę opakowania wokół mapy, która przedstawia pojedynczy interfejs mapy i zrobiłaby to, co trzeba, za pomocą +, - i iteratora.
Oto przykład, który okazał się nieco dłużej niż się spodziewali (tutaj sformatowana do wycinania i wklejania do 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]]())
}
i widzimy, że to działa zgodnie z oczekiwaniami tak:
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)
(gdybyś chciał odzyskać MapSet po ++, musiałbyś zastąpić ++, Hierarchia Map nie ma własnych budowniczych, aby zająć się takimi sprawami).
Cóż, nie mogę wymyślić żadnego sposobu poprawy tego, co masz. –