Если вам действительно не нужна неизменность, тогда, как говорили другие, MultiMap
- это путь. Если вам действительно нужна неизменность, то выбранный вами подход так же прост, как и все остальное; нет ничего встроенного (AFAIK), и любое создание неизменяемой MultiMap потребует гораздо больше работы, чем метод, который у вас есть.
Превосходство представления зависит от вашего использования. Вы часто хотите делать вещи со всеми значениями, соответствующими одному ключу? Можете ли вы вставить одно и то же значение несколько раз в вашу карту? Если да для обоих, ваше представление является правильным.
Если вы хотите, чтобы одно и то же значение вводилось не более одного раза на одну клавишу, вам следует использовать Set[U]
вместо Iterable[U]
(что можно легко сделать, добавив .toSet
к it.map(_._2)
).
Если вам не нравится иметь дело с наборами / итераторами и вы просто миритесь с этим (то есть на самом деле вы просто хотите иметь пары ключ-значение, а не пары ключ-набор-значение), вам придется написать класс-оболочку вокруг карта, которая представляет единый интерфейс карты и будет правильно работать с +, - и итератором.
Вот пример, который оказался немного длиннее, чем я ожидал (здесь отформатирован для вырезания и вставки в 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]]() )
}
и мы видим, что это работает так, как нужно:
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)
(хотя, если вы хотите вернуть MapSet после ++, вам нужно переопределить ++; иерархия Map не имеет своих собственных конструкторов, чтобы заботиться о таких вещах).