繁体   English   中英

Scala:更新地图内的数组

[英]Scala: Update Array inside a Map

我正在创建一个 Map,里面有一个 Array。 我需要不断向该数组添加值。 我怎么做?

var values: Map[String, Array[Float]] = Map()

我尝试了几种方法,例如:

myobject.values.getOrElse("key1", Array()).++(Array(float1))

几乎没有其他方法可以更新 Map 中的数组。

这段代码有问题:

values.getOrElse("key1", Array()).++(Array(float1))

这不会更新valuesMap ,它只是创建一个新的Array然后将其丢弃。

您需要替换原来的Map用新的,更新的Map ,就像这样:

values = values.updated("key1", values.getOrElse("key1", Array.empty[Float]) :+ float1)

要理解这一点,您需要清楚可变变量和可变数据之间的区别。

var用于创建一个可变变量,这意味着可以为该变量分配一个新值,例如

var name = "John"
name = "Peter" // Would not work if name was a val

相比之下,可变数据保存在其内容可以更改的对象中

val a = Array(1,2,3)
a(0) = 12 // Works even though a is a val not a var

在您的示例中, values是一个可变变量,但Map是不可变的,因此无法更改。 您必须创建一个新的、不可变的Map并将其分配给可变var

从我所看到的(根据++ ),您想附加Array ,还有一个元素。 但是Array固定长度结构,所以我建议使用Vector 因为,我想,您正在使用不可变的Map您也需要对其进行更新。

所以最终的解决方案可能如下所示:

var values: Map[String, Vector[Float]] = Map()
val key = "key1"
val value = 1.0
values = values + (key -> (values.getOrElse(key, Vector.empty[Float]) :+ value))

希望这可以帮助!

您可以使用 Scala 2.13 的转换函数以任何方式转换您的地图。

val values = Map("key" -> Array(1f, 2f, 3f), "key2" -> Array(4f,5f,6f))

values.transform {
  case ("key", v) => v ++ Array(6f)
  case (_,v) => v
}

结果:

Map(key -> Array(1.0, 2.0, 3.0, 6.0), key2 -> Array(4.0, 5.0, 6.0))

请注意,追加到数组需要线性时间,因此您可能需要考虑更有效的数据结构,例如VectorQueue甚至是List (如果您能负担得起前置而不是追加)。

更新

但是,如果您只想更新一个键,那么使用updatedWith可能会更好:

values.updatedWith("key")(_.map(_ ++ Array(6f)))

这将给出相同的结果。 上面代码的好处是,如果键不存在,它根本不会改变地图而不会抛出任何错误。

不可变与可变集合

您需要选择将使用immutablemutable集合的类型。 两者都很棒,而且工作方式完全不同 我猜您对mutable (来自其他语言)很熟悉,但immutable是 scala 中的默认值,并且您可能在代码中使用它(因为它不需要任何导入)。 不可变Map无法更改......您只能创建具有更新值的新Map (蒂姆和伊万的答案涵盖了这一点)。

解决您的问题的方法很少根据用例,所有方法都很好。

参见下面的实现(m1 到 m6):

//just for convenience 
type T = String
type E = Long

import scala.collection._

//immutable map with immutable seq (default).
var m1 = immutable.Map.empty[T,List[E]] 

//mutable map with immutable seq. This is great for most use-cases.
val m2 = mutable.Map.empty[T,List[E]]

//mutable concurrent map with immutable seq.
//should be fast and threadsafe (if you know how to deal with it) 
val m3 = collection.concurrent.TrieMap.empty[T,List[E]] 

//mutable map with mutable seq.
//should be fast but could be unsafe. This is default in most imperative languages (PHP/JS/JAVA and more).
//Probably this is what You have tried to do
val m4 = mutable.Map.empty[T,mutable.ArrayBuffer[E]] 

//immutable map with mutable seq.
//still could be unsafe 
val m5 = immutable.Map.empty[T,mutable.ArrayBuffer[E]] 
//immutable map with mutable seq v2 (used in next snipped)
var m6 = immutable.Map.empty[T,mutable.ArrayBuffer[E]]

//Oh... and NEVER DO THAT, this is wrong
//I mean... don't keep mutable Map in `var`
//var mX = mutable.Map.empty[T,...]

其他答案显示immutable.Mapimmutable.Seq ,这是首选方式(或至少默认)。 它需要一些费用,但对于大多数应用程序来说完全没问题。 这里有关于不可变数据结构的很好的信息来源: https : //stanch.github.io/reftree/talks/Immutability.html

每个变体都有自己的优点和缺点。 每个人都以不同的方式处理更新,这使得这个问题乍一看要困难得多

解决方案

val k = "The Ultimate Answer"
val v = 42f

//immutable map with immutable seq (default).
m1 = m1.updated(k, v :: m1.getOrElse(k, Nil))

//mutable map with immutable seq.
m2.update(k, v :: m2.getOrElse(k, Nil))

//mutable concurrent map with immutable seq.
//m3 is bit harder to do in scala 2.12... sorry :)

//mutable map with mutable seq.
m4.getOrElseUpdate(k, mutable.ArrayBuffer.empty[Float]) += v

//immutable map with mutable seq.
m5 = m5.updated(k, {
  val col = m5.getOrElse(k, c.mutable.ArrayBuffer.empty[E])
  col += v
  col
})

//or another implementation of immutable map with mutable seq.
m6.get(k) match {
  case None => m6 = m6.updated(k, c.mutable.ArrayBuffer(v))
  case Some(col) => col += v
}

使用此实现检查 Scalafiddle https://scalafiddle.io/sf/WFBB24j/3 这是一个很棒的工具(ps:您可以随时按CTRL+S保存您的更改并共享链接以编写有关您的代码段的问题)。

哦...如果您关心并发性( m3案例),那么再写一个问题。 这样的话题应该在单独的线程中:)

(im)mutable api VS (im)mutable Collections

您可以拥有可变集合并仍然使用将复制原始 seq 的不可变 api。 例如Array是可变的:

val example = Array(1,2,3) 
example(0) = 33 //edit in place
println(example.mkString(", ")) //33, 2, 3

但是它上面的一些函数(例如++ )会创建新的序列……不会改变现有的序列:

val example2 = example ++ Array(42, 41) //++ is immutable operator
println(example.mkString(", ")) //33, 2, 3  //example stays unchanged
println(example2.mkString(", ")) //33, 2, 3, 42, 41 //but new sequence is created

有一个方法updateWith是可变的,并且存在于可变序列中。 还有updatedWith并且它同时存在于不可变和可变集合中,如果您不够小心,您将使用错误的集合(是的……多 1 个字母)。

这意味着你需要小心你正在使用哪些函数,不可变的还是可变的。 大多数情况下,您可以按结果类型区分它们。 如果有东西返回集合,那么它可能是原始 seq 的某种副本。 结果是单位,那么它肯定是可变的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM