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

2022-08-31 14:59:06

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

为此选择了几种语言,例如C#,Java和Python。如果它的目的是节省内存或提高比较等操作的效率,那么它对串联和其他修改操作有什么影响?


答案 1

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

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

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

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

不可变类型有几个小缺点:

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

但总的来说,不变性的优点远远超过了次要的缺点。即使您只对性能感兴趣,复制的并发优势和廉价性通常也会使不可变字符串比具有锁定和防御性复制的可变字符串性能更高。


答案 2

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

当您进行多线程处理时,它变得更加重要。不可变数据可以在线程之间安全地传递,而可变数据会引起无休止的麻烦。

基本上,不可变数据使处理它的代码更容易推理。这就是为什么纯粹的函数式语言试图保持一切不可变。