[英]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))
这不会更新values
的Map
,它只是创建一个新的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))
请注意,追加到数组需要线性时间,因此您可能需要考虑更有效的数据结构,例如Vector
或Queue
甚至是List
(如果您能负担得起前置而不是追加)。
更新:
但是,如果您只想更新一个键,那么使用updatedWith
可能会更好:
values.updatedWith("key")(_.map(_ ++ Array(6f)))
这将给出相同的结果。 上面代码的好处是,如果键不存在,它根本不会改变地图而不会抛出任何错误。
您需要选择将使用immutable
或mutable
集合的类型。 两者都很棒,而且工作方式完全不同。 我猜您对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.Map
和immutable.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
案例),那么再写一个问题。 这样的话题应该在单独的线程中:)
您可以拥有可变集合并仍然使用将复制原始 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.