Scala 设置不变性

Scala Sets Immutability

scala> var immSet = Set("A", "B")
immSet: scala.collection.immutable.Set[String] = Set(A, B)

scala> immSet += "C"

scala> println(immSet)
Set(A, B, C)

我想知道,允许 var 与不可变 Set 一起使用有什么好处?在这种情况下我不会失去不变性吗?

What is the advantage I am getting by allowing var to be used with with a Immutabable Sets?

我想说这主要会引起混乱。您使用 var 的事实允许您 覆盖变量 ,但是 Set 本身 不会改变,它分配一个带有附加值"C"的新集合。但是由于您使用的是 var,现在不再引用之前的 Set,除非您引用的是堆栈中更高的其他地方:

scala> var firstSet = Set("A", "B")
firstSet: scala.collection.immutable.Set[String] = Set(A, B)

scala> var secondSet = firstSet
secondSet: scala.collection.immutable.Set[String] = Set(A, B)

scala> firstSet += "C"

scala> firstSet
res6: scala.collection.immutable.Set[String] = Set(A, B, C)

scala> secondSet
res7: scala.collection.immutable.Set[String] = Set(A, B)

因为 secondSet 仍然指向由 firstSet 创建的 Set,我们没有看到反映的值更新。我认为使这个不可变增加了底层 Set 不可变以及指向它的变量的清晰度。当您使用 val 时,如果您尝试重新分配,编译器会大喊大叫,迫使您意识到已初始化一个新集合。

关于不变性,我们需要把它分成两部分。有 Set 的不变性,还有指向 Set 的变量的不变性,这是两个不同的东西。你用这种方法失去了后者。

如果您想了解定义为 var 的不可变集合和定义为 val 的可变集合之间的基本区别,请阅读@YuvalItzchakov 的回答。我将专注于这两种方法的实际方面。

首先,这两种方法都意味着可变性。如果你想留下来 "pure functional" 你应该避免其中任何一个。

现在,如果你想要可变集合,最好的方法是什么?简短的回答,这取决于。

  1. 性能。可变集合通常比不可变集合更快。这意味着,如果您的可变变量以某种方式包含(例如,不转义私有方法),则使用 val c = MutableCollection() 可能更好。 Collections API 中的大多数 Scala 方法在内部使用可变集合。

  2. 线程安全。不可变集合的值始终是线程安全的。您可以将它发送到另一个线程,而不考虑可见性和并发更改。

    var a = ImmutableCol()
    otherThreadProcessor.process(a)
    a += 1 // otherThread will still have previous value
    

    另一方面,如果你想从多线程修改集合,最好使用Java的并发集合API。

  3. 代码清晰。想象一下,您有一些函数将集合作为参数,然后以某种方式对其进行修改。如果集合是可变的,那么在函数 returns 之后,作为参数传递的集合将保持修改状态。

    def recImmutable(a:Set[Int]): Unit = {
      var b = a
      b += 4
    }
    
    val a = Set(2,3)
    recImmutable(a)
    println(a)
    // prints Set(2, 3)
    
    def recMutable(a:mutable.Set[Int]): Unit = {
      var b = a
      b += 4
    }
    
    val b = mutable.Set(2,3)
    recMutable(b)
    println(b)
    // prints Set(2, 3, 4)