【问题标题】:Why do we have String class, if StringBuilder or StringBuffer can do what a String does? [duplicate]为什么我们有 String 类,如果 StringBuilder 或 StringBuffer 可以做 String 做的事? [复制]
【发布时间】:2015-08-01 17:16:27
【问题描述】:

我一直想知道为什么 JAVA 和 C# 有 String(不可变和线程安全)类,如果它们有 StringBuilder(可变和非线程安全)或 StringBuffer(可变和线程安全)类。 StringBuilder/StringBuffer 不是 String 类的超集吗?我的意思是,如果我可以选择使用StringBuilder/StringBuffer,我为什么要使用String 类?

例如,而不是使用以下,

String str;

为什么我不能总是使用关注?

StringBuilder strb; //or
StringBuffer strbu;

简而言之,我的问题是,如果我将 String 替换为 StringBuffer 类,我的代码将如何生效?此外,StringBuffer 增加了可变性的优势。

【问题讨论】:

  • 在某些情况下,使用字符串而不是字符串生成器是有益的。记住字符串是轻量级的。我只是找到一些可以为您提供证据的链接。
  • 享元是什么意思?
  • 哈?字符串的使用方式很多,拥有这个不可变的类是件好事。 StringBuilder 和 -Buffer 用于创建/构建字符串 - 为什么要使用它们来替换字符串?我不明白你为什么不明白!
  • @Abhishek 看看here。 String 的实习性质使其更加熟练。
  • @JenishRabadiya 知道了。因此,String 将只为每个不同的字符串文字制作一份副本。这可能是个问题,我的意思是如果字符串池被填满会发生什么?

标签: java c#


【解决方案1】:

我想在StringStringBuilder 类之间添加一些区别:

是的,如上所述,String 是不可变类,创建字符串后无法更改内容。它允许使用来自不同线程的相同字符串对象而无需锁定。 如果您需要将很多字符串连接在一起,请使用StringBuilder 类。当您使用“+”运算符时,它会在托管堆上创建大量字符串对象并损害性能。

StringBuilder 是可变类。 StringBuilder 将字符存储在数组中,并且可以在不创建新字符串对象的情况下对字符进行操作(例如添加、删除、替换、追加)。 如果您知道结果字符串的大致长度,您应该设置容量。默认容量为 16 (.NET 4.5)。它可以提高性能,因为StringBuilder 具有内部字符数组。当字符数超过当前容量时,重新创建字符数组。

【讨论】:

    【解决方案2】:

    许多答案已经概述了使用可变变体(例如StringBuilder)存在缺点。为了说明这个问题,使用StringBuilder 无法实现的一件事是关联内存,即哈希表。当然,大多数实现都允许您使用StringBuilder 作为哈希表的键,但它们只会找到与StringBuilder 完全相同的实例的值。但是,您想要实现的典型行为是字符串来自何处并不重要,因为只有字符很重要,例如您从数据库或文件(或任何其他外部资源)中读取字符串。

    但是,据我了解您的问题,您主要是在询问字段类型。确实,我认为您的观点特别考虑到我们正在对其他对象的集合执行完全相同的操作,这些对象通常不是不可变对象而是可变集合,例如分别在 C# 或 Java 中的 ListArrayList。归根结底,字符串只是字符的集合,为什么不让它可变呢?

    我在这里给出的答案是,如何更改此类字符串的通常行为与通常的集合非常不同。如果你有一个后续元素的集合,那么通常只向集合中添加一个元素,而不影响集合的大部分,即你不会丢弃一个列表来插入一个项目,至少除非你在 Haskell 中编程:)。对于许多像名称这样的字符串,这是不同的,因为您通常会替换整个字符串。考虑到字符串数据类型的重要性,平台通常会为字符串提供很多优化,例如内部字符串,使得选择更加偏向于字符串。

    然而,最后,每个程序都是不同的,你可能有一些要求,使默认使用StringBuilder 更合理,但由于给定的原因,我认为这些情况相当罕见。

    编辑:正如你所要求的例子。考虑以下代码:

    stopwatch.Start();
    var s = "";
    for (int i = 0; i < 100000; i++)
    {
        s = "." + s;
    }
    stopwatch.Stop();
    Console.WriteLine(stopwatch.ElapsedMilliseconds);
    
    stopwatch.Restart();
    var s2 = new StringBuilder();
    for (int i = 0; i < 100000; i++)
    {
         s2.Insert(0, ".");
    }
    stopwatch.Stop();
    Console.WriteLine(stopwatch.ElapsedMilliseconds);
    

    从技术上讲,这两个位都在做非常相似的事情,它们会在第一个位置插入一个字符并移动后面的任何内容。这两个版本都将涉及复制以前存在的整个字符串。在我的机器上,string 的版本在 1750 毫秒内完成,而 StringBuilder 则用了 2245 毫秒。但是,这两个版本都相当快,在这种情况下对性能的影响可以忽略不计。

    【讨论】:

    • 你说的优化。你的意思是什么优化?我的意思是 String 的性能比 StringBuffer 好吗?如何?什么时候?你能分享一下例子吗?
    • 我更新了答案以包含一个示例。但是,我不知道在此优化中究竟做了什么。
    • 不过,我刚查了一下,这似乎不是真正的优化,一个愚蠢的字符列表大约需要 1000 毫秒。
    • 没错!所以,我还是不明白为什么要选择String 而不是StringBuffer。如果需要,像这样的简单追加/插入我将使用 char 数组,正如你所说:)
    • 也许另一个论点是,一旦你开始对字符串做一些事情,在大多数应用程序中你将需要它们作为string。如果您一直使用StringBuffer,您将需要不是真正节省内存的转换,但正如我所说,如果您确定这是最适合您的应用程序,请继续。
    【解决方案3】:

    我的意思是,如果我可以选择使用 StringBuilder/StringBuffer,我为什么要使用 String 类?

    正是因为它是不可变的。不变性有一大堆好处host,主要是它可以更容易地推理你的代码,而无需在任何地方创建数据副本,“以防万一”某些东西决定改变值。例如:

    private readonly String name;
    
    public Person(string name)
    {
        if (string.IsNullOrEmpty(name)) // Or whatever
        {
            // Throw some exception
        }
        this.name = name;
    }
    
    // All the rest of the code can rely on name being a non-null 
    // reference to a non-empty string. Nothing can mutate it, leaving
    // evil reflection aside.
    

    不变性使共享变得简单而高效。这对于多线程代码特别有用。它使“修改”(即使用不同数据创建新实例)更加痛苦,但在许多情况下这绝对没问题,因为值通过系统而不会被修改。

    不变性特别对字符串、日期、数字等“简单”类型(BigDecimalBigInteger 等)很有用。它允许它们更容易地在地图中使用,它允许简单的相等定义等。

    【讨论】:

    • 我可以用private readonly StringBuffer name; 替换private readonly String name;,不是吗?它将如何影响我的代码?我的意思是,我通过StringBuffer 获得了想要的结果,并增加了可变性的优势。那么,我为什么要使用String
    • @Abhishek:“并增加了可变性的优势”——不,这是一个劣势。这意味着有人可以使用最初通过我的验证标准的构建器调用Person p = new Person(builder);,然后在没有Person 类有任何发言权的情况下对其进行修改。为了避免这种情况,Person 类需要复制经过验证的数据。可变性是一个巨大的问题,而不是优势。软件设计的关键是管理复杂性——而大规模的可变性是它的一大敌人。
    • 如果不变性只是一个优势,我们将不再看到像 Java 或 C# 这样的命令式代码,而只会看到函数式语言。如果可变性只是一个优势,我们根本不会看到函数式语言。由于两者都很受欢迎,因此两者都不是。
    • @Georg:是的,这就是为什么我说它让修改变得更加痛苦。但是对于某些类型(例如String),不变性的好处远远超过了大多数用途的缺点。我认为java.util.Date 和几个类似的类应该是不可变的......
    • @Abhishek:这是一种允许程序员管理复杂性的方法,从而更容易推理他们的代码。这远远超出了调试范围。
    【解决方案4】:

    1) StringBuilderStringBuffer 都是 mutable。所以它会导致一些问题,比如在hashMap 中的键等集合中使用。见this link

    另一个不变性优势的例子是Jon 在他的 cmets 中提到的。我只是在这里粘贴。

    有人可以使用最初通过我的验证标准的构建器调用Person p = new Person(builder);,然后再对其进行修改,而 Person 类没有任何发言权。为了避免这种情况,Person 类需要复制经过验证的数据。

    不变性确保不会发生这种情况。

    2) 由于string 是java 中使用最广泛的对象,string pool 提供重用相同的字符串,从而节省内存。

    【讨论】:

    • 如果线程安全是问题,我可以使用StringBuffer。 String 如何帮助重用内存?我的意思是StringBufferStringBuilder 对象也存储在内存位置,可以重复使用。
    • @Abhishek 您将需要编写一个特定的函数来搜索内存并为您返回字符串,现在假设在某些地方字符串已更改,所有使用它的实例都会更改,这可能不会要求。这里又来了immutability 的字符串有用。这是最大的因素
    • 我将使用StringBuffer(线程安全),所以我不必担心有人更改数据。
    【解决方案5】:

    我完全同意Jon Skeet 的观点,即不变性是使用String 的原因之一。另一个原因(从 C# 的角度来看)是 String 实际上比 StringBuilder 更轻。如果您查看StringString Builder 的参考源,您将看到StringBuilder 实际上有许多String 常量。作为开发人员,您应该只使用您需要的东西,因此除非您需要 StringBuilder 提供的额外好处,否则您应该使用 String

    【讨论】:

      【解决方案6】:

      String

      • 是不可变的(因此您可以在集合中使用它)
      • 每个操作都会在堆上创建一个新实例。从技术上讲真的取决于代码。

      出于性能和内存消耗的目的,使用StringBuilder 是有意义的。

      【讨论】:

      • 不一定。字符串文字将在可能的情况下被实习。
      • 正如我所说的取决于代码...优化不是问题的一部分。
      • 那么为什么要发布有关它的错误信息?但这不是优化。它是在语言中指定的字符串文字被池化。
      猜你喜欢
      • 2023-04-04
      • 2011-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-26
      • 1970-01-01
      • 2011-02-24
      • 2017-09-04
      相关资源
      最近更新 更多