为什么字符串在许多编程语言中是不可变的?

sno*_*fox 59 c++ java string immutability

可能重复:
为什么字符串在Java和.NET中不可变?
为什么.NET String是不可变的?

为此选择了几种语言,例如C#,Java和Python.如果它旨在为比较等操作节省内存或提高效率,它对连接和其他修改操作有什么影响?

mik*_*era 73

不可变类型通常是一件好事:

  • 它们更好地兼顾并发性(您不需要锁定无法更改的内容!)
  • 它们可以减少错误:当你不期望它可以引入各种奇怪的错误("远距离行动")时,可变对象很容易被改变
  • 它们可以安全地共享(即对同一对象的多个引用),这可以减少内存消耗并提高缓存利用率.
  • 如果你必须采取可变对象的防御性副本,共享也会使复制成为非常便宜的O(1)操作,如果它是O(n).这是一个大问题,因为复制是一个非常常见的操作(例如,每当你想传递参数....)

因此,使字符串不可变是一种非常合理的语言设计选择.

有些语言(特别是像Haskell和Clojure这样的函数式语言)甚至更进一步,几乎所有东西都是不可变的.如果您对不变性的好处感兴趣,这个有启发性的视频非常值得一看.

不可变类型有一些小的缺点:

  • 创建更改字符串(如连接)的操作更昂贵,因为您需要构造新对象.通常,对于连接两个不可变字符串,成本为O(n + m),但如果使用基于树的字符串数据结构(如Rope),它可以低至O(log(m + n)).另外,如果你真的需要有效地连接字符串,你总是可以使用像Java的StringBuilder这样的特殊工具.
  • 对大字符串进行小的更改可能导致需要构造一个全新的大型String副本,这显然会增加内存消耗.但请注意,这通常不是垃圾收集语言中的一个大问题,因为如果您不保留对它的引用,旧副本将很快收集垃圾.

总的来说,不变性的优势远远超过了微小的劣势.即使你只对性能感兴趣,复制的并发优势和便宜性通常会使不可变字符串比具有锁定和防御复制的可变字符串更高效.

  • 如果字符串是可变的,考虑到大量使用,它将在大型项目中创建许多难以检测的错误.这些错误的成本只会使字符串连接CPU消耗的问题可以忽略不计.此外,在许多情况下,即使字符串是可变的,在进行轻微修改时仍需要创建不同的字符串. (4认同)
  • @Nawaz:取决于你对快速的定义,但绝对肯定,如果你快速解释为"足够快,你没有理由在实践中关心".如果你想迂腐并说"尽可能快",那么不,但这显然是一个毫无意义的学术论点.如果它不是瓶颈,则延迟无关紧要. (3认同)
  • 好点.但是,每枚硬币都有两面.你刚刚列出了好处.缺点是什么?因为它是不可变的,所以我们不能简单地通过相互挂起来连接两个字符串; 我们还必须创建一个新的字符串,即使它只与现有字符串中的char一样少,这意味着消耗cpu的内存分配. (2认同)
  • @Nawaz:这很有道理.在极有可能的情况下,你的String是在年轻一代,那么它将很快被清除.至少在JVM上你可以通过转义分析将它分配到堆栈上以便立即释放.即使它没有快速释放,你也不在乎*因为它确保在你再次需要内存之前被释放.GC是你的朋友,人们真的需要学会不再担心它. (2认同)

Mic*_*rdt 16

它主要用于防止编程错误.例如,字符串经常用作哈希表中的键.如果它们可以更改,则哈希表将被破坏.这只是一个例子,当你使用它时,有一个数据变化会导致问题.安全性是另一个:如果你在执行他们请求的操作之前检查是否允许用户访问给定路径的文件,那么包含路径的字符串最好不是可变的......

当你进行多线程处理时,它变得更加重要.不可变数据可以在线程之间安全地传递,而可变数据会导致无休止的头痛.

基本上,不可变数据使得在其上工作的代码更容易推理.这就是纯函数式语言试图保持一切不可变的原因.