it-swarm.cn

String vs. StringBuilder

我理解StringStringBuilder之间的区别(StringBuilder是可变的)但两者之间是否有很大的性能差异?

我正在研究的程序有很多案例驱动的字符串附加(500+)。使用StringBuilder是更好的选择吗?

201
Kuvo

是的,性能差异很大。请参阅知识库文章“ 如何在Visual C#中提高字符串连接性能 ”。

我总是先尝试编码以获得清晰度,然后再针对性能进行优化。这比反过来要容易得多!但是,看到两者之间的应用程序存在巨大的性能差异,我现在仔细考虑一下。

幸运的是,在代码上运行性能分析以查看您花费时间的位置,然后根据需要修改它以使用StringBuilder相对简单。

223
Jay Bazuzi

为了澄清Gillian关于4字符串的内容,如果你有这样的话:

string a,b,c,d;
 a = b + c + d;

那么使用字符串和加号运算符会更快。这是因为(就像Eric指出的那样),它在内部自动使用StringBuilder(实际上,它使用StringBuilder也使用的原语)

但是,如果您正在做的更接近:

string a,b,c,d;
 a = a + b;
 a = a + c;
 a = a + d;

然后你需要显式使用StringBuilder。 .Net不会在这里自动创建StringBuilder,因为它毫无意义。在每一行的末尾,“a”必须是(不可变的)字符串,因此它必须在每一行上创建和配置StringBuilder。为了速度,你需要使用相同的StringBuilder,直到你完成构建:

string a,b,c,d;
StringBuilder e = new StringBuilder();
 e.Append(b);
 e.Append(c);
 e.Append(d);
 a = e.ToString();
54
James Curran

StringBuilder是首选 _如果_ 你正在做代码传递中的多个循环或分叉...但是,对于PURE性能,如果你可以使用 _ single _ string声明,那么性能更高。

例如:

string myString = "Some stuff" + var1 + " more stuff"
                  + var2 + " other stuff" .... etc... etc...;

比表现更高效

StringBuilder sb = new StringBuilder();
sb.Append("Some Stuff");
sb.Append(var1);
sb.Append(" more stuff");
sb.Append(var2);
sb.Append("other stuff");
// etc.. etc.. etc..

在这种情况下,StringBuild可以被认为是更易于维护的,但是不比单个字符串声明更高效。

10次​​中有9次...使用字符串生成器。

一方面注意:string + var也比内部使用StringBuilder的string.Format方法(通常)更高效(当有疑问时......检查反射器!)

28
calebjenkins

此基准测试表明,当组合3个或更少的字符串时,常规连接更快。

http://www.chinhdo.com/20070224/stringbuilder-is-not-always-faster/

StringBuilder可以显着提高内存使用率,尤其是在将500个字符串组合在一起的情况下。

请考虑以下示例:

string buffer = "The numbers are: ";
for( int i = 0; i < 5; i++)
{
    buffer += i.ToString();
}
return buffer;

记忆中会发生什么?创建以下字符串:

1 - "The numbers are: "
2 - "0"
3 - "The numbers are: 0"
4 - "1"
5 - "The numbers are: 01"
6 - "2"
7 - "The numbers are: 012"
8 - "3"
9 - "The numbers are: 0123"
10 - "4"
11 - "The numbers are: 01234"
12 - "5"
13 - "The numbers are: 012345"

通过将这五个数字添加到字符串的末尾,我们创建了13个字符串对象!其中12个没用!哇!

StringBuilder修复了这个问题。它不是我们经常听到的“可变字符串”( .NET中的所有字符串都是不可变的 )。它的工作原理是保留一个内部缓冲区,一个char数组。调用Append()或AppendLine()会将字符串添加到char数组末尾的空白处;如果数组太小,它会创建一个新的更大的数组,并在那里复制缓冲区。因此,在上面的示例中,StringBuilder可能只需要一个数组来包含字符串的所有5个添加项 - 具体取决于其缓冲区的大小。你可以告诉StringBuilder它的缓冲区在构造函数中应该有多大。

23
Matt Trunnell

使用String concatenation vs StringBuilder时演示速度差异的简单示例:

System.Diagnostics.Stopwatch time = new Stopwatch();
string test = string.Empty;
time.Start();
for (int i = 0; i < 100000; i++)
{
    test += i;
}
time.Stop();
System.Console.WriteLine("Using String concatenation: " + time.ElapsedMilliseconds + " milliseconds");

结果:

使用字符串连接:15423毫秒

StringBuilder test1 = new StringBuilder();
time.Reset();
time.Start();
for (int i = 0; i < 100000; i++)
{
    test1.Append(i);
}
time.Stop();
System.Console.WriteLine("Using StringBuilder: " + time.ElapsedMilliseconds + " milliseconds");

结果:

使用StringBuilder:10毫秒

结果,第一次迭代花费了15423 ms,而使用StringBuilder的第二次迭代花费了10 ms。

在我看来,使用StringBuilder更快,更快。

19
Diizzy

是的,StringBuildername__在对字符串执行重复操作时提供更好的性能。这是因为所有更改都是针对单个实例进行的,因此可以节省大量时间,而不是创建像Stringname__这样的新实例。

String Vs Stringbuilder

  • Stringname__

    1. Systemname__名称空间下
    2. 不可变(只读)实例
    3. 当连续发生值变化时,性能会下降
    4. 线程安全
  • StringBuildername__(可变字符串)

    1. System.Text命名空间下
    2. 可变实例
    3. 显示更好的性能,因为对现有实例进行了新的更改

强烈推荐dotnet mob文章: C#中的String Vs StringBuilder

相关的Stack Overflow问题: 字符串在C#中没有变化时字符串的可变性?

10
Shamseer K

StringBuilder减少了分配和分配的数量,但代价是使用了额外的内存。如果使用得当,它可以完全消除编译器反复分配越来越大的字符串直到找到结果的需要。

string result = "";
for(int i = 0; i != N; ++i)
{
   result = result + i.ToString();   // allocates a new string, then assigns it to result, which gets repeated N times
}

vs.

String result;
StringBuilder sb = new StringBuilder(10000);   // create a buffer of 10k
for(int i = 0; i != N; ++i)
{
   sb.Append(i.ToString());          // fill the buffer, resizing if it overflows the buffer
}

result = sb.ToString();   // assigns once
8
moswald

String或StringBuilder对象的串联操作的性能取决于内存分配发生的频率。字符串连接操作始终分配内存,而StringBuilder连接操作仅在StringBuilder对象缓冲区太小而无法容纳新数据时分配内存。因此,如果连接固定数量的String对象,则String类更适合并置操作。在这种情况下,编译器甚至可以将单个连接操作组合成单个操作。如果连接任意数量的字符串,则StringBuilder对象最好用于连接操作;例如,如果循环连接随机数量的用户输入字符串。

资料来源: _ msdn _

4
user487069

String Vs String Builder:

首先要知道的是,这两个班级在哪个大会中存在?

所以,

string 存在于System命名空间中。

StringBuilder 存在于System.Text名称空间中。

对于 字符串 声明:

您必须包含System命名空间。这样的事情。 Using System;

对于 StringBuilder 声明:

您必须包含System.text命名空间。这样的事情。 Using System.text;

现在来看实际问题。

string StringBuilder ?之间有什么区别?

这两者之间的主要区别在于:

string 是不可变的。

StringBuilder 是可变的。

所以现在让我们讨论不可变可变之间的区别

可变: :表示Changable。

不可变: :表示不可长。

例如:

using System;

namespace StringVsStrigBuilder
{
    class Program
    {
        static void Main(string[] args)
        {
            // String Example

            string name = "Rehan";
            name = name + "Shah";
            name = name + "RS";
            name = name + "---";
            name = name + "I love to write programs.";

            // Now when I run this program this output will be look like this.
            // output : "Rehan Shah RS --- I love to write programs."
        }
    }
}

所以在这种情况下,我们将改变相同的对象5次。

所以明显的问题是!当我们改变相同的字符串5次时,实际发生了什么。

这是当我们将相同的字符串更改5次时发生的事情。

让我们看看这个数字。

enter image description here

解释: /

当我们首先将此变量“name”初始化为“Rehan”i-e string name = "Rehan"时,此变量将在堆栈“name”上创建并指向该“Rehan”值。执行此行后:“name = name +”Shah“。引用变量不再指向该对象”Rehan“它现在指向”Shah“,依此类推。

所以string是不可变的意味着一旦我们在内存中创建了对象,我们就无法改变它们。

因此,当我们连接name变量时,前一个对象保留在内存中,另一个新的字符串对象被创建...

因此,从上图中我们有五个对象,四个对象被扔掉,根本没有使用它们。它们仍留在记忆中,它们占据了大量的记忆。 “垃圾收集器”负责那个如此干净的内存资源。

因此,在字符串的情况下,当我们一遍又一遍地操纵字符串时,我们有一些很多的对象创建并保留在内存中。

所以这是字符串变量的故事。

现在让我们看看StringBuilder对象。 例如:

using System;
using System.Text;

namespace StringVsStrigBuilder
{
    class Program
    {
        static void Main(string[] args)
        {
            // StringBuilder Example

            StringBuilder name = new StringBuilder();
            name.Append("Rehan");
            name.Append("Shah");
            name.Append("RS");
            name.Append("---");
            name.Append("I love to write programs.");


            // Now when I run this program this output will be look like this.
            // output : "Rehan Shah Rs --- I love to write programs."
        }
    }
}

所以在这种情况下,我们将改变相同的对象5次。

所以明显的问题是!当我们改变相同的StringBuilder 5次时,实际上发生了什么。

当我们将同一个StringBuilder更改5次时,这就是重要的事情。

让我们看看这个数字。 enter image description here

解释: 在StringBuilder对象的情况下。你不会得到新的对象。同一个对象将在内存中进行更改,因此即使您更改对象并说10,000次,我们仍然只有一个stringBuilder对象。

您没有很多垃圾对象或non_referenced stringBuilder对象,因为它可以更改。它是可变的意味着它会在一段时间内发生变化吗?

差异: /

  • String存在于System命名空间中,其中Stringbuilder存在于System.Text命名空间中。
  • string是不可变的,而StringBuilder是mutabe。
3
Rehan Shah

StringBuilder更适合从许多非常量值构建字符串。

如果您正在构建一个来自许多常量值的字符串,例如HTML或XML文档中的多行值或其他文本块,那么只需添加相同的字符串即可,因为几乎所有编译器都会“常量折叠”,当你有一堆常量操作时减少解析树的过程(当你写一些类似int minutesPerYear = 24 * 365 * 60时也会使用它)。对于非常量值相互附加的简单情况,.NET编译器会将代码减少到与StringBuilder类似的代码。

但是当你的追加不能被编译器简化为更简单的东西时,你需要一个StringBuilder。正如fizch指出的那样,这更有可能发生在循环内部。

3
JasonTrue
2
Jim G.

使用字符串进行连接可能会导致O(n^2)顺序的运行时复杂性。

如果使用StringBuilder,则必须完成的内存复制要少得多。使用StringBuilder(int capacity),如果可以估计最终String的大小,可以提高性能。即使你不准确,你可能只需要增加StringBuilder的容量几次,这也有助于提高性能。

2
Steve g

我已经看到在StringBuilder的实例上使用EnsureCapacity(int capacity)方法调用之前显着的性能提升,然后将其用于任何字符串存储。我通常在实例化之后在代码行上调用它。它具有与如下实例化StringBuilder相同的效果:

var sb = new StringBuilder(int capacity);

此调用提前分配所需的内存,这会在多个Append()操作期间导致更少的内存分配。你必须对你需要多少内存进行有根据的猜测,但对于大多数应用程序来说,这应该不会太困难。我通常错误地记忆太多(我们说的是1k左右)。

2
Jason Jackson

我相信如果你有超过4个字符串需要一起追加,StringBuilder会更快。此外,它可以做一些很酷的事情,如AppendLine。

2
Gilligan

在.NET中,StringBuilder仍然比附加字符串更快。我很确定在Java中,当你追加字符串时,它们只是在引擎盖下创建一个StringBuffer,所以这并没有什么区别。我不确定为什么他们还没有在.NET中做过这个。

2
Eric Z Beard

String和StringBuilder实际上都是不可变的,StringBuilder内置了缓冲区,可以更有效地管理它的大小。当StringBuilder需要调整大小时,它是在堆上重新分配的时候。默认情况下,它的大小为16个字符,您可以在构造函数中设置它。

例如。

StringBuilder sb = new StringBuilder(50);

1
capgpilk

除了之前的答案之外,在考虑这样的问题时,我总是首先要做的是创建一个小的测试应用程序。在这个应用程序内部,为两个场景执行一些计时测试,并亲自看看哪个更快。

恕我直言,附加500多个字符串条目肯定应该使用StringBuilder。

1
RichS

字符串连接将花费更多。在Java中,您可以根据需要使用StringBuffer或StringBuilder。如果您想要一个同步和线程安全的实现,请转到StringBuffer。这将比字符串连接更快。

如果您不需要同步或线程安全实现,请转到StringBuilder。这将比String连接更快,也比StringBuffer更快,因为它们没有同步开销。

1
raffimd

StringBuilder效率明显提高,但除非你进行大量的字符串修改,否则你不会看到这种性能。

下面是一段代码,用于举例说明性能。正如您所看到的那样,当您进入大型迭代时,您才真正开始看到主要的性能提升。

正如您所看到的,200,000次迭代耗时22秒,而使用StringBuilder的100万次迭代几乎是即时的。

string s = string.Empty;
StringBuilder sb = new StringBuilder();

Console.WriteLine("Beginning String + at " + DateTime.Now.ToString());

for (int i = 0; i <= 50000; i++)
{
    s = s + 'A';
}

Console.WriteLine("Finished String + at " + DateTime.Now.ToString());
Console.WriteLine();

Console.WriteLine("Beginning String + at " + DateTime.Now.ToString());

for (int i = 0; i <= 200000; i++)
{
    s = s + 'A';
}

Console.WriteLine("Finished String + at " + DateTime.Now.ToString());
Console.WriteLine();
Console.WriteLine("Beginning Sb append at " + DateTime.Now.ToString());

for (int i = 0; i <= 1000000; i++)
{
    sb.Append("A");
}
Console.WriteLine("Finished Sb append at " + DateTime.Now.ToString());

Console.ReadLine();

上述代码的结果:

开始字符串+在28/01/2013 16:55:40。

完成字符串+在28/01/2013 16:55:40。

开始字符串+在28/01/2013 16:55:40。

完成字符串+在28/01/2013 16:56:02。

Sb的开头追加于28/01/2013 16:56:02。

完成的Sb追加于28/01/2013 16:56:02。

0
CathalMF

作为一般经验法则,如果我必须多次设置字符串的值,或者如果字符串有任何附加,那么它必须是字符串构建器。我已经看过过去编写的应用程序,然后才开始学习字符串构建器,这些字符串构建器具有巨大的内存占用空间,而且似乎在不断增长和增长。更改这些程序以使用字符串生成器可显着降低内存使用量。现在我发誓字符串建设者。

0
user13288

我的方法一直是在连接4个或更多字符串时使用StringBuilder OR当我不知道如何进行连接时。

这里有好的相关文章

0
JohnC

StringBuilder可能更可取。原因是它分配了比当前需要更多的空间(您设置了字符数),以便为将来的附加留出空间。然后那些适合当前缓冲区的未来附加不需要任何内存分配或垃圾收集,这可能很昂贵。通常,我使用StringBuilder进行复杂的字符串连接或多重格式化,然后在数据完成时转换为普通的String,并且我想再次使用不可变对象。

0
deemer

如果你正在进行大量的字符串连接,请使用StringBuilder。当您使用String连接时,每次都会创建一个新的String,从而占用更多内存。

亚历克斯

0
Alex Fort