Scala 不可变映射,何时变为可变?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10141654/
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
Scala immutable map, when to go mutable?
提问by virtualeyes
My present use case is pretty trivial, either mutable or immutable Map will do the trick.
我目前的用例非常简单,可变或不可变 Map 都可以解决问题。
Have a method that takes an immutable Map, which then calls a 3rd party API method that takes an immutable Map as well
有一个采用不可变 Map 的方法,然后调用一个采用不可变 Map 的第 3 方 API 方法
def doFoo(foo: String = "default", params: Map[String, Any] = Map()) {
val newMap =
if(someCondition) params + ("foo" -> foo) else params
api.doSomething(newMap)
}
The Map in question will generally be quite small, at most there might be an embedded List of case class instances, a few thousand entries max. So, again, assume little impact in going immutable in this case (i.e. having essentially 2 instances of the Map via the newMap val copy).
有问题的 Map 通常很小,最多可能有一个嵌入的案例类实例列表,最多几千个条目。因此,再次假设在这种情况下不可变几乎没有影响(即通过 newMap val 副本基本上具有 Map 的 2 个实例)。
Still, it nags me a bit, copying the map just to get a new map with a few k->v entries tacked onto it.
尽管如此,它还是让我有点烦,复制地图只是为了得到一张新地图,上面有几个 k->v 条目。
I could go mutable and params.put("bar", bar), etc. for the entries I want to tack on, and then params.toMapto convert to immutable for the api call, that is an option. but then I have to import and pass around mutable maps, which is a bit of hassle compared to going with Scala's default immutable Map.
params.put("bar", bar)对于我想要添加的条目,我可以使用可变和等,然后params.toMap为 api 调用转换为不可变,这是一个选项。但随后我必须导入并传递可变映射,与使用 Scala 的默认不可变映射相比,这有点麻烦。
So, what are the general guidelines for when it is justified/good practice to use mutable Map over immutable Maps?
那么,在不可变 Map 上使用可变 Map 是合理/良好实践的一般准则是什么?
Thanks
谢谢
EDITso, it appears that an add operation on an immutable map takes near constant time, confirming @dhg's and @Nicolas's assertion that a full copy is not made, which solves the problem for the concrete case presented.
编辑因此,似乎在不可变映射上的添加操作需要几乎恒定的时间,确认@dhg 和@Nicolas 的断言,即未制作完整副本,这解决了所呈现的具体案例的问题。
回答by dhg
Depending on the immutable Map implementation, adding a few entries may not actually copy the entire original Map. This is one of the advantages to the immutable data structure approach: Scala will try to get away with copying as little as possible.
根据不可变的 Map 实现,添加一些条目实际上可能不会复制整个原始 Map。这是不可变数据结构方法的优点之一:Scala 会尽量避免复制。
This kind of behavior is easiest to see with a List. If I have a val a = List(1,2,3), then that list is stored in memory. However, if I prepend an additional element like val b = 0 :: a, I doget a new 4-element Listback, but Scala did notcopy the orignal list a. Instead, we just created one new link, called it b, and gave it a pointer to the existing List a.
这种行为最容易通过List. 如果我有一个val a = List(1,2,3),那么该列表将存储在内存中。但是,如果我在前面加上一个额外的元素一样val b = 0 :: a,我做得到一个新的4元List回来,但斯卡拉并没有复制一部开拓创新的列表a。相反,我们只是创建了一个新链接,称为b,并为其提供了一个指向现有 List 的指针a。
You can envision strategies like this for other kinds of collections as well. For example, if I add one element to a Map, the collection could simply wrap the existing map, falling back to it when needed, all while providing an API as if it were a single Map.
您也可以为其他类型的集合设想这样的策略。例如,如果我向 a 添加一个元素Map,该集合可以简单地包装现有地图,在需要时回退到它,同时提供一个 API,就好像它是单个Map.
回答by paradigmatic
Using a mutable object is not bad in itself, it becomes bad in a functional programming environment, where you try to avoid side-effects by keeping functions pure and objects immutable.
使用可变对象本身并不坏,它在函数式编程环境中变得很糟糕,在那里你试图通过保持函数纯和对象不可变来避免副作用。
However, if you create a mutable object inside a function and modify this object, the function is still pure if you don't release a reference to this object outside the function. It is acceptable to have code like:
但是,如果您在函数内部创建了一个可变对象并修改了该对象,那么如果您不在函数外部释放对该对象的引用,该函数仍然是纯的。可以接受如下代码:
def buildVector( x: Double, y: Double, z: Double ): Vector[Double] = {
val ary = Array.ofDim[Double]( 3 )
ary( 0 ) = x
ary( 1 ) = y
ary( 2 ) = z
ary.toVector
}
Now, I think this approach is useful/recommended in two cases: (1) Performance, if creating and modifying an immutable object is a bottleneck of your whole application; (2) Code readability, because sometimes it's easier to modify a complex object in place (rather than resorting to lenses, zippers, etc.)
现在,我认为这种方法在两种情况下有用/推荐:(1)性能,如果创建和修改不可变对象是整个应用程序的瓶颈;(2) 代码可读性,因为有时就地修改一个复杂的对象更容易(而不是求助于镜头、拉链等)
回答by Nicolas
In addition to dhg's answer, you can take a look to the performance of the scala collections. If an add/remove operation doesn't take a linear time, it must do something else than just simply copying the entire structure. (Note that the converse is not true: it's not beacuase it takes linear time that your copying the whole structure)
除了 dhg 的回答,你还可以看看scala collections的表现。如果添加/删除操作不需要线性时间,则它必须执行其他操作,而不仅仅是简单地复制整个结构。(请注意,反过来是不正确的:这不是因为复制整个结构需要线性时间)
回答by Andrew Norman
I like to use collections.maps as the declared parameter types (input or return values) rather than mutable or immutable maps. The Collections maps are immutable interfaces that work for both types of implementations. A consumer method using a map really doesn't need to know about a map implementation or how it was constructed. (It's really none of its business anyway).
我喜欢使用 collections.maps 作为声明的参数类型(输入或返回值)而不是可变或不可变的映射。Collections 映射是不可变的接口,适用于两种类型的实现。使用地图的消费者方法实际上不需要了解地图实现或它是如何构造的。(反正这真的不关它的事)。
If you go with the approach of hiding a map's particular construction (be it mutable or immutable) from the consumers who use it then you're still getting an essentially immutable map downstream. And by using collection.Map as an immutable interface you completely remove all the ".toMap" inefficiency that you would have with consumers written to use immutable.Map typed objects. Having to convert a completely constructed map into another one simply to comply to an interface not supported by the first one really is absolutely unnecessary overhead when you think about it.
如果您采用对使用它的消费者隐藏地图的特定构造(无论是可变的还是不可变的)的方法,那么您仍然会在下游获得本质上不可变的地图。并且通过使用 collection.Map 作为不可变接口,您可以完全消除所有“.toMap”效率低下的情况,而这些低效率在编写为使用 immutable.Map 类型对象的消费者时会出现。必须将一个完全构造的映射转换为另一个映射,只是为了遵守第一个不支持的接口,当您考虑它时,这确实是绝对不必要的开销。
I suspect in a few years from now we'll look back at the three separate sets of interfaces (mutable maps, immutable maps, and collections maps) and realize that 99% of the time only 2 are really needed (mutable and collections) and that using the (unfortunately) default immutable map interface really adds a lot of unnecessary overhead for the "Scalable Language".
我怀疑几年后我们会回顾三个独立的接口集(可变映射、不可变映射和集合映射),并意识到 99% 的时间只有 2 个是真正需要的(可变和集合)和使用(不幸的是)默认的不可变映射接口确实为“可扩展语言”增加了很多不必要的开销。

