Scala 集合,单键多值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10571709/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 04:10:21  来源:igfitidea点击:

Scala collections, single key multiple values

scalacollectionsmap

提问by Nabegh

I have a list of parent keys, each of which could possibly have zero or more associated values. I am not sure which collection to use.

我有一个父键列表,每个键都可能有零个或多个关联值。我不确定要使用哪个集合。

I am using Map[Int,List[String]]

我在用 Map[Int,List[String]]

I am declaring the Map as

我将地图声明为

var nodes = new HashMap[Int, List[String]]

Then I have two methods to handle adding new elements. The first is to add new keys addNodeand the second is to add new values addValue. Initially, the key will not have any values associated with it. Later on, during execution, new values will be associated.

然后我有两种方法来处理添加新元素。第一个是添加新键addNode,第二个是添加新值addValue。最初,键不会有任何关联的值。稍后,在执行期间,将关联新值。

def addNode(key: Int) = nodes += (key -> "")

def addValue(key: Int, value: String) = ???

I am not sure how to implement addValues

我不知道如何实施 addValues

Update:

更新:

In response to @oxbow-lakes answer, This is the error I am receiving. Please note that keys need not have values associated with them.

回应@oxbow-lakes 的回答,这是我收到的错误。请注意,键不需要具有与其关联的值。

scala> var nodes = Map.empty[Int, List[String]]
nodes: scala.collection.immutable.Map[Int,List[String]] = Map()

scala> nodes += (1->null)

scala> nodes += (1 -> ("one" :: (nodes get 1 getOrElse Nil)))
java.lang.NullPointerException
    at .<init>(<console>:9)
    at .<clinit>(<console>)
    at .<init>(<console>:11)
    at .<clinit>(<console>)
    at $print(<console>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:704)
    at scala.tools.nsc.interpreter.IMain$Request$$anonfun.apply(IMain.scala:920)
    at scala.tools.nsc.interpreter.Line$$anonfun.apply$mcV$sp(Line.scala:43)
    at scala.tools.nsc.io.package$$anon.run(package.scala:25)
    at java.lang.Thread.run(Thread.java:680)

Update 2:

更新 2:

The problem with the code above is the line nodes += (1->null)the key should be associated with Nilinstead. Below is the working code.

上面代码的问题是nodes += (1->null)应该与键相关联的那一行Nil。下面是工作代码。

scala> var nodes = Map.empty[Int, List[String]]
nodes: scala.collection.immutable.Map[Int,List[String]] = Map()

scala> nodes += (1->Nil)

scala> nodes += (1 -> ("one" :: (nodes get 1 getOrElse Nil)))

scala> nodes
res27: scala.collection.immutable.Map[Int,List[String]] = Map(1 -> List(one))

回答by oxbow_lakes

Using MultiMap

使用多图

You possibly want to use MultiMap, which is a mutable collection isomorphic to Map[K, Set[V]]. Use as follows:

您可能想要使用MultiMap,这是一个与 同构的可变集合Map[K, Set[V]]。使用方法如下:

import collection.mutable
val mm = new mutable.HashMap[Int, mutable.Set[String]] with mutable.MultiMap[Int, String]

Then you add your nodes:

然后添加节点:

mm addBinding (key, value)

Without MultiMap

没有 MultiMap

The alternative is to stick with immutable values. Assuming you want to avoid using lenses(see scalaz), you can add nodes as follows:

另一种方法是坚持使用不可变的值。假设您想避免使用镜头(参见scalaz),您可以按如下方式添加节点:

nodes += (key -> (value :: (nodes get key getOrElse Nil)))

Here it is working (in response to your comment):

它正在工作(响应您的评论):

scala> var nodes = Map.empty[Int, List[String]]
nodes: scala.collection.immutable.Map[Int,List[String]] = Map()

scala> def addNode(key: Int, value: String) =
     | nodes += (key -> (value :: (nodes get key getOrElse Nil)))
addNode: (key: Int, value: String)Unit

scala> addNode(1, "Hi")

scala> addNode(1, "Bye")

scala> nodes
res2: scala.collection.immutable.Map[Int,List[String]] = Map(1 -> List(Bye, Hi))

Using Scalaz

使用 Scalaz

Using the scalaz library, you can realize that this is simply using the Emptypattern:

使用 scalaz 库,您可以意识到这只是使用Empty模式:

nodes += (key -> (value :: ~(nodes get key)))

Or you could take advantage of the fact that Mapis a monoid:

或者你可以利用Map一个幺半群的事实:

nodes = nodes |+| Map(key -> List(value))

回答by dhg

In addition to @oxbow_lakes' answer, here's a idea for how you could use an addMapmethod that correctly adds two maps together (ie, combining lists for matching keys, adding new lists for new keys):

除了@oxbow_lakes 的回答之外,这里还有一个关于如何使用addMap将两个映射正确添加在一起的方法的想法(即,组合匹配键的列表,为新键添加新列表):

class EnhancedListMap(self: Map[Int,List[String]]) {
  def addMap(other: Map[Int,List[String]]) =
    (this.ungroup ++ enhanceListMap(other).ungroup)
      .groupBy(_._1)
      .mapValues(_.map(_._2))

  def ungroup() =
    self.toList.flatMap{ case (k,vs) => vs.map(k -> _) }
}

implicit def enhanceListMap(self: Map[Int,List[String]]) = new EnhancedListMap(self)

And you'd use it like this:

你会像这样使用它:

val a = Map(1 -> List("a","b"), 2 -> List("c","d"))
val b = Map(2 -> List("e","f"), 3 -> List("g","h"))
a addMap b
//Map(3 -> List(g, h), 1 -> List(a, b), 2 -> List(c, d, e, f))

You can include addNode, addValue, and addValuesthe same way (to EnhancedListMapabove):

你可以包括addNode, addValue, 和addValues同样的方式(到EnhancedListMap上面):

  def addNode(key: Int) =
    if(self contains key) self else self + (key -> Nil)

  def addValue(key: Int, value: String) =
    self + (key -> (value :: (self get key getOrElse Nil)))

  def addValues(key: Int, values: List[String]) =
    self + (key -> (values ::: (self get key getOrElse Nil)))

And then use them together:

然后一起使用它们:

var nodes = Map.empty[Int, List[String]]             
// Map()
nodes = nodes.addNode(1)                             
// Map(1 -> List())
nodes = nodes.addValue(1,"a")                        
// Map(1 -> List(a))
nodes = nodes.addValue(2,"b")                        
// Map(1 -> List(a), 2 -> List(b))
nodes = nodes.addValues(2,List("c","d"))             
// Map(1 -> List(a), 2 -> List(c, d, b))
nodes = nodes.addValues(3,List("e","f"))             
// Map(1 -> List(a), 2 -> List(c, d, b), 3 -> List(e, f))
nodes = nodes.addMap(Map(3 -> List("g","h"), 4-> List("i","j")))
// Map(1 -> List(a), 2 -> List(c, d, b), 3 -> List(e, f, g, h), 4 -> List(i, j))

回答by jcsahnwaldt says GoFundMonica

I quite like the getOrElseUpdatemethod provided by mutable maps:

我非常喜欢getOrElseUpdate可变映射提供的方法:

import scala.collection.mutable._

private val nodes = new HashMap[Int, Buffer[String]]

def addNode(key: Int): Unit =
  nodes.getOrElseUpdate(key, new ArrayBuffer)

def addValue(key: Int, value: String): Unit  =
  nodes.getOrElseUpdate(key, new ArrayBuffer) += value