26

我目前的用例非常简单,可变或不可变 Map 都可以解决问题。

有一个采用不可变 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)
}

有问题的地图通常很小,最多可能有一个嵌入的案例类实例列表,最多几千个条目。因此,再次假设在这种情况下不可变影响不大(即通过 newMap val 副本基本上拥有 2 个 Map 实例)。

尽管如此,它还是让我有点烦,复制地图只是为了得到一张新地图,上面贴了一些 k->v 条目。

对于我想要附加的条目,我可以使用 mutable 和params.put("bar", bar)等,然后params.toMap为 api 调用转换为不可变的,这是一个选项。但是我必须导入和传递可变映射,与使用 Scala 的默认不可变映射相比,这有点麻烦。

那么,什么时候使用可变地图而不是不可变地图是合理/良好的做法的一般准则是什么?

谢谢

编辑 所以,在不可变地图上的添加操作似乎花费了几乎恒定的时间,证实了@dhg 和@Nicolas 的断言,即没有制作完整的副本,这解决了所提出的具体案例的问题。

4

4 回答 4

42

根据不可变 Map 的实现,添加一些条目实际上可能不会复制整个原始 Map。这是不可变数据结构方法的优点之一:Scala 会尽量避免复制。

这种行为最容易用List. 如果我有一个val a = List(1,2,3),那么该列表将存储在内存中。但是,如果我在前面加上一个额外的元素val b = 0 :: a,我得到一个新的 4 元素List,但 Scala 没有复制原始列表a。相反,我们只是创建了一个新链接,称为它b,并给它一个指向现有 List 的指针a

您也可以为其他类型的集合设想类似的策略。例如,如果我向 a 添加一个元素Map,则该集合可以简单地包装现有地图,在需要时回退到它,同时提供一个 API,就好像它是单个Map.

于 2012-04-13T13:27:13.027 回答
14

使用可变对象本身并不坏,在函数式编程环境中它变得不好,在这种环境中,您试图通过保持函数纯净和对象不可变来避免副作用。

但是,如果您在函数内部创建一个可变对象并修改此对象,则如果您不在函数外部释放对该对象的引用,则该函数仍然是纯的。可以接受如下代码:

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
}

现在,我认为这种方法在两种情况下有用/推荐:(1)性能,如果创建和修改不可变对象是整个应用程序的瓶颈;(2) 代码可读性,因为有时在原地修改复杂对象更容易(而不是求助于镜头、拉链等)

于 2012-04-13T15:00:28.850 回答
5

除了 dhg 的回答,你可以看看scala collections 的表现。如果添加/删除操作不需要线性时间,则它必须执行其他操作,而不仅仅是简单地复制整个结构。(请注意,反过来是不正确的:这不是因为复制整个结构需要线性时间)

于 2012-04-13T13:50:40.337 回答
0

我喜欢使用 collections.maps 作为声明的参数类型(输入或返回值),而不是可变或不可变映射。Collections 映射是不可变的接口,适用于这两种类型的实现。使用地图的消费者方法实际上不需要了解地图的实现或它是如何构造的。(无论如何,这真的不关它的事)。

如果您采用向使用它的消费者隐藏地图的特定构造(无论是可变的还是不可变的)的方法,那么您仍然会在下游获得本质上不可变的地图。并且通过使用 collection.Map 作为不可变接口,您可以完全消除编写使用 immutable.Map 类型对象的消费者所具有的所有“.toMap”低效率。必须将一个完全构建的映射转换为另一个映射,以符合第一个不支持的接口,当您考虑它时,这确实是绝对不必要的开销。

我怀疑在几年后,我们将回顾三组独立的接口(可变映射、不可变映射和集合映射),并意识到 99% 的时间只有两个是真正需要的(可变和集合)和使用(不幸的是)默认的不可变映射接口确实为“可扩展语言”增加了许多不必要的开销。

于 2016-06-03T17:54:25.087 回答