【问题标题】:What's the advantage of a String being Immutable?字符串不可变的优点是什么?
【发布时间】:2011-03-25 08:36:03
【问题描述】:

有一次我研究了字符串不可变的优点,因为它可以提高内存中的性能。

谁能给我解释一下?我在网上找不到。

【问题讨论】:

  • What is meant by immutable? 的可能副本。虽然该问题的标题不一样,但它包含了一个关于优缺点的问题,至少有一个答案(由 Imagist 提供)涵盖了这一点。

标签: string immutability


【解决方案1】:

不变性(对于字符串或其他类型)有很多优点:

  • 它可以更轻松地对代码进行推理,因为您可以对变量和参数进行假设,而这些假设是您无法通过其他方式做出的。
  • 它简化了多线程编程,因为从无法更改的类型中读取始终可以安全地并发执行。
  • 它允许将相同的值组合在一起并从多个位置引用,从而减少内存使用量。 Java 和 C# 都执行字符串实习,以减少嵌入代码中的文字字符串的内存成本。
  • 它简化了某些算法(例如采用回溯或值空间分区的算法)的设计和实现,因为之前计算的状态可以在以后重复使用。
  • 不变性是许多函数式编程语言的基本原则 - 它允许将代码视为从一种表示到另一种表示的一系列转换,而不是一系列突变。

不可变字符串也有助于避免使用字符串作为缓冲区的诱惑。 C/C++ 程序中的许多缺陷与使用裸字符数组组合或修改字符串值导致的缓冲区溢出问题有关。将字符串视为可变类型会鼓励使用更适合缓冲区操作的类型(请参阅 .NET 或 Java 中的 StringBuilder)。

【讨论】:

  • 但是如果您使用Copy-On-Write 范式,您将拥有不可变字符串的优势 私有副本和就地修改(如果需要),例如用于快速解析。 StringBuilder 只是减缓不可变字符串连接的一种解决方法,对于与高效堆管理系统相关的 COW 模式几乎没有什么好处。 COW 可以让您的缓冲区访问安全同时快速。
  • 其中几个参数似乎将“const”参数(与 Java“final”相同)与引用对象的不变性混淆。
  • @LBushkin,尽管您在要点中提出了一些很好的论据,但您的“不可变字符串也有助于避免......”段落具有误导性。 StringBuilder 解决的问题与 C 中的缓冲区覆盖完全不同 - 它只是对字符串连接的优化,因为连接不可变字符串很慢 - 您需要创建两个新对象并为每个连接步骤复制所有字符。
  • @ArnaudBouchez,COW 没有不可变对象的所有优点,它只是为字符串的大多数情况提供了一些性能提升。然而,“它使推理代码变得更容易”——不可变对象的这种优势是 COW 模式所不具备的。
  • @Alexey:Copy-on-write 在可以指定变量s1s2 类型为String 的语言中可能会有所帮助,每个变量都应该是宇宙中任何地方的唯一引用StringRef 的单独实例,并且 s1=s2; 应作为 s1.CopyFrom(s2); 执行,而不更改 StringRef s1 所指的实例。 Java 和 .NET 语言不支持这样的概念,但 C++ 支持(至少在不必与其他 .NET 语言交互的代码中)
【解决方案2】:

考虑替代方案。 Java 没有 const 限定符。如果 String 对象是可变的,那么您传递对字符串的引用的任何方法都可能具有修改字符串的副作用。不可变字符串消除了防御性副本的需要,并降低了程序错误的风险。

【讨论】:

    【解决方案3】:

    不可变字符串的复制成本很低,因为您不需要复制所有数据 - 只需复制指向数据的引用或指针即可。

    任何类型的不可变类都更容易在多个线程中使用,唯一需要的同步是销毁。

    【讨论】:

      【解决方案4】:

      也许,我的回答已经过时了,但可能有人会在这里找到新信息。

      为什么Java String 是不可变的以及为什么它是好的:

      • 您可以在线程之间共享一个字符串,并确保没有人会更改字符串并混淆另一个线程
      • 您不需要锁。多个线程可以使用不可变字符串而不会发生冲突
      • 如果您刚刚收到一个字符串,您可以确定此后没有人会更改其值
      • 您可以有许多重复的字符串——它们将指向一个实例,只指向一个副本。这样可以节省计算机内存 (RAM)
      • 您可以在不复制的情况下创建子字符串 – 通过创建指向现有字符串元素的指针。这就是为什么Java子字符串操作实现如此之快的原因
      • 不可变字符串(对象)更适合用作哈希表中的键

      【讨论】:

        【解决方案5】:

        a) 想象一下没有使字符串不可变的 StringPool 设施,这根本不可能,因为在字符串池的情况下,一个字符串对象/文字,例如“测试”已被许多引用变量引用,因此如果其中任何一个更改值,其他人将自动受到影响,即假设 字符串 A =“测试”和字符串 B =“测试” 现在字符串 B 称为 "Test".toUpperCase() 将同一对象更改为 "TEST" ,因此 A 也将是 "TEST",这是不可取的。
        b) 为什么String在Java中不可变的另一个原因是允许String缓存它的hashcode,Java中的不可变String缓存它的hash码并且每次调用String的hashcode方法时都不计算,这使得它作为hashmap key非常快.

        【讨论】:

          【解决方案6】:

          想一想坐在一个公共池上的各种字符串。字符串变量然后指向池中的位置。如果你复制一个字符串变量,originalcopy 共享相同的字符。这些共享的效率超过了通过提取子字符串和连接来编辑字符串的低效率。

          【讨论】:

            【解决方案7】:

            从根本上说,如果一个对象或方法希望将信息传递给另一个对象或方法,有几种方法可以做到:

            1. 它可以提供对包含信息的可变对象的引用,并且接收者承诺永远不会修改。

            2. 它可能会引用一个包含数据但不关心其内容的对象。

            3. 它可以将信息存储到预期数据接收者知道的可变对象中(通常由该数据接收者提供)。

            4. 它可能会返回对包含信息的不可变对象的引用。

            在这些方法中,#4 是迄今为止最简单的。在许多情况下,可变对象比不可变对象更容易使用,但是没有简单的方法可以与“不可信”代码共享可变对象中的信息,而无需先将信息复制到某个东西别的。相比之下,保存在不可变对象中的信息可以通过简单地共享该引用的副本来轻松共享。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-09-28
              • 1970-01-01
              • 2020-09-25
              • 1970-01-01
              • 1970-01-01
              • 2017-10-06
              • 1970-01-01
              相关资源
              最近更新 更多