C#中string和StringBuilder的区别

77

stringStringBuilder 有什么区别?

此外,有哪些例子可以帮助理解?


1
可能是字符串 vs StringBuilder的重复问题。 - George Stocker
另请参阅:https://dev59.com/6nNA5IYBdhLWcg3wjOhS http://stackoverflow.com/questions/665499/string-is-immutable-and-stringbuilder-is-mutable https://dev59.com/6U7Sa4cB1Zd3GeqP4IWq - George Stocker
5
第一个其他问题实际上非常狭窄,甚至以“我已经知道区别”开始 - 这正是这个问题特别要求的。 - Joey
1
@George:我简直不敢相信那个问题没有出现在相关侧边栏中。 - Bill the Lizard
@Bill:使用正确的标签后情况有所改善,编辑标题后效果更佳。 - Joey
1
@Johannes:这样更好。我同意这不完全与其他链接的问题相同,但这可能只是因为这个问题有点模糊。 - Bill the Lizard
13个回答

113

string实例是不可变的。创建后无法更改。任何看起来会改变字符串的操作都会返回一个新实例:

string foo = "Foo";
// returns a new string instance instead of changing the old one
string bar = foo.Replace('o', 'a');
string baz = foo + "bar"; // ditto here

Immutable对象有一些好的特性,例如它们可以在不担心同步问题的情况下跨线程使用,或者您可以直接分配私有备份字段而不担心其他人更改不应更改的对象(例如数组或可变列表,如果不需要则通常需要复制它们)。但是,如果不小心使用,则可能会创建严重的性能问题(几乎任何东西都是如此——如果您需要一个以执行速度自豪的语言的示例,请查看C的字符串操作函数)。

当您需要一个可变字符串(例如,您正在逐步构建的字符串或其中修改了许多内容)时,您将需要一个StringBuilder,它是可以更改的字符缓冲区。这主要会影响性能。如果您想要一个可变字符串,而使用普通的string实例进行操作,那么您将不必要地创建和销毁许多对象,而StringBuilder实例本身将发生更改,从而避免了许多新对象的需要。

简单的例子:以下内容将使许多程序员感到痛苦:

string s = string.Empty;
for (i = 0; i < 1000; i++) {
  s += i.ToString() + " ";
}

在这里,您将创建2001个字符串,其中2000个将被丢弃。使用StringBuilder的相同示例:

StringBuilder sb = new StringBuilder();
for (i = 0; i < 1000; i++) {
  sb.Append(i);
  sb.Append(' ');
}

这应该会大大减轻内存分配器的压力 :-)

但需要注意的是,当涉及到字符串时,C#编译器是相当智能的。例如,以下代码行:

string foo = "abc" + "def" + "efg" + "hij";

编译器会将其连接起来,使得运行时只剩下一个字符串。类似的,如下所示的代码:

string foo = a + b + c + d + e + f;

将被重写为

string foo = string.Concat(a, b, c, d, e, f);

这样你就不必支付五个无意义的字符串连接,这是处理该问题的天真方式。但这并不能在上面提到的循环中为你节省开销(除非编译器展开了循环,但我认为只有JIT可能会这样做,最好不要抱有此期望)。


任何看起来会改变字符串的操作都会返回一个新实例,但为什么呢?原因是什么?为什么字符串不可变?为什么CLR会创建新实例并删除先前的实例? - AminM
5
@Jeson: 不可变数据结构具有许多优点,其中大部分与默认编写更安全的代码相关。您无需担心字符串会因为其他线程对它们进行操作而发生更改。您也不必返回类内部字符串的副本以防止使用者篡改您的字段。 - Joey

13

String vs. StringBuilder

  • String

    • 在 System 命名空间下

    • 不可变(只读)实例

    • 当连续更改值时性能会降低

    • 线程安全

  • StringBuilder(可变字符串)

    1. 在 System.Text 命名空间下
    2. 可变实例
    3. 由于新更改是针对现有实例进行的,因此表现更好

有关此主题的描述性文章,并使用ObjectIDGenerator的许多示例,请访问此链接

相关的 Stack Overflow 问题:当字符串在 C# 中不变时,字符串的可变性


7

字符串是不可变的,这意味着当你创建一个字符串后,你永远不能改变它。相反,它会创建一个新的字符串来存储新值,如果你需要频繁更改字符串变量的值,则这可能效率低下。

StringBuilder可以用来模拟可变字符串,因此在需要经常更改字符串时非常有用。


6

字符串

字符串实例是不可变的,这意味着创建之后就不能更改。如果我们对一个字符串执行任何操作,它将返回一个新的实例(在内存中创建一个新实例),而不是修改现有实例的值。

字符串生成器

字符串生成器是可变的,也就是说,如果我们对字符串生成器执行任何操作,它会更新现有实例的值,而不会创建一个新的实例。

字符串和字符串生成器的区别


3

主要区别:

String是不可变的。这意味着您根本无法修改字符串;修改的结果是一个新字符串。如果您计划添加到字符串中,则此方法并不有效。

StringBuilder是可变的。它可以以任何方式进行修改,而且不需要创建新实例。完成工作后,可以调用ToString()来获取字符串。

字符串可以参与内部化。这意味着具有相同内容的字符串可能具有相同的地址。StringBuilder不能被内部化。

String是唯一可以具有引用字面量的类。


2
StringBuilder会在需要分多步构建字符串时给你帮助。
不要这样做:
String x = "";
x += "first ";
x += "second ";
x += "third ";

you do

StringBuilder sb = new StringBuilder("");
sb.Append("first ");
sb.Append("second ");
sb.Append("third");
String x = sb.ToString();

最终的效果是相同的,但 StringBuilder 将使用更少的内存并运行得更快。它不会创建一个新的字符串作为两个字符串的连接,而是分别创建这些块,只有在最后才将它们合并。

2
StringBuilder类的文档中可以得知:
String对象是不可变的。每次使用System.String类中的方法时,都会在内存中创建一个新的字符串对象,这需要为该新对象分配新的空间。在需要对字符串执行重复修改的情况下,创建新的String对象所产生的开销可能很大。当您想要修改字符串而不创建新对象时,可以使用System.Text.StringBuilder类。例如,在循环中连接许多字符串时,使用StringBuilder类可以提高性能。

2

字符串是不可变的,如果你改变它们的值,旧值将被丢弃并在堆上创建一个新值。而在字符串构建器中,我们可以修改现有的值而不会创建新值。

因此,从性能方面考虑,字符串构建器是有益的,因为我们不需要占用更多的内存空间。


1

字符串的连接复杂度为O(N2),而StringBuffer的复杂度为O(N)。

因此,在循环中使用连接操作可能会导致性能问题,因为每次都会创建大量新对象。


0

如果您想要在字符串生成器中迭代字符串,可以使用Clone方法...该方法会返回一个对象,因此您可以使用ToString方法将其转换为字符串...:)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接