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

时间:2012-03-03 06:51:49

标签: java c++ string immutability

  

可能重复:
  Why can't strings be mutable in Java and .NET?
  Why .NET String is immutable?

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

4 个答案:

答案 0 :(得分:73)

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

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

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

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

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

  • 创建更改字符串(如连接)的操作更昂贵,因为您需要构造新对象。通常,对于连接两个不可变字符串,成本为O(n + m),但如果使用基于树的字符串数据结构(如Rope),则它可以低至O(log(m + n))。另外,如果你真的需要有效地连接字符串,你总是可以使用像Java StringBuilder这样的特殊工具。
  • 对大字符串的小改动可能导致需要构造一个全新的大字符串副本,这显然会增加内存消耗。但请注意,这在垃圾收集语言中通常不是一个大问题,因为如果您不保留对它的引用,旧副本将很快收集垃圾。
总的来说,不变性的优势远远超过了微小的劣势。即使你只对性能感兴趣,复制的并发性和便宜性通常会使不可变字符串比具有锁定和防御性复制的可变字符串更高效。

答案 1 :(得分:16)

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

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

基本上,不可变数据使得在其上运行的代码更容易推理。这就是为什么纯函数式语言试图保持所有不可变的原因。

答案 2 :(得分:3)

在Java中,不仅String,而且所有原始Wrapper类(Integer,Double,Character等)都是不可变的。我不确定确切的原因,但我认为这些是所有编程方案都适用的基本数据类型。如果他们改变,事情就会变得疯狂。更具体地说,我将使用一个示例:假设您已经打开了与远程主机的套接字连接。主机名是String,端口是Integer。如果在建立连接后修改这些值,该怎么办?

就性能而言,Java从名为Literal Pool的单独内存部分为这些类分配内存,而不是从堆栈或堆中分配内存。 Literal Pool是索引的,如果你使用字符串“String”两次,它们会指向Literal池中的同一个对象。

答案 3 :(得分:0)

将字符串作为不可变也允许新的字符串引用容易,因为相同/相似的字符串将可以从先前创建的字符串池中获得。从而降低了新对象创建的成本。