我理解StringBuilder的好处。
但是如果我想连接2个字符串,那么我认为没有StringBuilder它会更好(更快)。这是对的吗?
在什么时候(字符串数量)使用StringBuilder会变得更好?
答案 0 :(得分:69)
我热烈建议你阅读杰夫阿特伍德的The Sad Tragedy of Micro-Optimization Theater。
它将Simple Concatenation与StringBuilder与其他方法相比较。
现在,如果您想查看一些数字和图表,请点击链接;)
答案 1 :(得分:38)
但如果我想要结合2 字符串,然后我认为它是 没有更好(更快)的做法 StringBuilder的。这是对的吗?
这确实是正确的,你可以找到为什么得到了很好的解释:
http://www.yoda.arachsys.com/csharp/stringbuilder.html
总结:如果你可以像
一样结合字符串var result = a + " " + b + " " + c + ..
没有StringBuilder你最好只在复制时(最终计算得到的字符串的长度。);
对于像
这样的结构var result = a;
result += " ";
result += b;
result += " ";
result += c;
..
每次都会创建新对象,因此您应该考虑使用StringBuilder。
最后,文章总结了这些经验法则:
经验法则
那么,什么时候应该使用StringBuilder, 什么时候应该使用字符串 连接运算符?
当使用StringBuilder时一定要使用 你是一个非平凡的联系人 循环 - 特别是如果你不知道 肯定(在编译时)有多少 迭代你将通过 环。例如,读取文件a 一次性格,建立一个 使用+ =运算符时的字符串 是潜在的表现自杀。
绝对使用连接 操作员,你可以(可读) 指定需要的一切 在一个声明中连接起来。 (如果你 有一系列的东西 连接,考虑调用 String.Concat显式 - 或 String.Join如果你需要一个分隔符。)
不要害怕打破文字 分成几个连接位 - 结果会一样。你可以帮忙 打破长文字的可读性 例如,用几行代表 没有损害表现。
如果您需要中间结果 某事的串联 除了提供下一次迭代 串联,StringBuilder不是 去帮助你。例如,如果 你从第一个建立一个全名 名称和姓氏,然后添加一个 第三条信息( 昵称,也许)到最后,你会 只受益于使用StringBuilder 如果你不需要(名字+ 姓氏)字符串用于其他目的 (就像我们在创建的例子中所做的那样 一个Person对象)。
如果你只是有几个连接 要做,你真的想做 在单独的陈述中,它没有 你走哪条路真的很重要哪一个 方式更有效将取决于 大小的连接数 涉及的字符串,以及什么顺序 他们是连在一起的。如果你真的 相信这段代码是一个 性能瓶颈,简介或 以两种方式对它进行基准测试。
答案 2 :(得分:11)
System.String是一个不可变对象 - 这意味着无论何时修改其内容,它都会分配一个新字符串,这需要时间(和内存?)。 使用StringBuilder可以修改对象的实际内容,而无需分配新的内容。
因此,当您需要对字符串进行许多修改时,请使用StringBuilder。
答案 3 :(得分:8)
不是......你应该使用StringBuilder,如果你连接大字符串,或者你有许多连接,就像在循环中一样。
答案 4 :(得分:4)
但是如果我想连接2个字符串,那么我认为没有StringBuilder这样做会更好更快。这是对的吗?
是。但更重要的是,在这种情况下使用香草String
会更加可读。另一方面,在循环中使用它是有意义的,也可以像连接一样可读。
我会警惕将特定数量的连接作为阈值引用的经验法则。在循环中使用它(和仅循环)可能同样有用,更容易记忆并且更有意义。
答案 5 :(得分:4)
没有确定的答案,只有经验法则。我自己的个人规则是这样的:
StringBuilder
。StringBuilder
。StringBuilder
。答案 6 :(得分:4)
解释
然后你要数到三,不多,不少。三个应该是你要计算的数字,计数的数量应该是三个。四,你不算数,不计算你两个,除了你接着三个。一旦达到第三个数字,即为第三个数字,那么你就可以将你的圣手榴弹放在安提阿身上
我通常将字符串构建器用于任何代码块,这将导致三个或更多字符串的串联。
答案 7 :(得分:4)
这是一个简单的测试应用程序来证明这一点:
class Program
{
static void Main(string[] args)
{
const int testLength = 30000;
var StartTime = DateTime.Now;
//TEST 1 - String
StartTime = DateTime.Now;
String tString = "test string";
for (int i = 0; i < testLength; i++)
{
tString += i.ToString();
}
Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
//result: 2000 ms
//TEST 2 - StringBuilder
StartTime = DateTime.Now;
StringBuilder tSB = new StringBuilder("test string");
for (int i = 0; i < testLength; i++)
{
tSB.Append(i.ToString());
}
Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
//result: 4 ms
Console.ReadLine();
}
}
<强>结果:强>
30&#39; 000次迭代
1000次迭代
500次迭代
答案 8 :(得分:3)
只要您可以在物理上输入连接数(a + b + c ...),它就不应该有很大的不同。 N平方(N = 10)是100倍减速,这不应该太糟糕。
最大的问题是当你连接数百个字符串时。在N = 100时,你会减速10000倍。这非常糟糕。
答案 9 :(得分:2)
我认为在何时使用或何时不使用之间存在细微差别。当然,除非有人进行了一些广泛的测试以证明黄金条件。
对我来说,如果只连接两个巨大的字符串,我将不会使用StringBuilder。如果存在具有不确定性计数的循环,我很可能,即使循环可能很小。
答案 10 :(得分:1)
单个串联不值得使用StringBuilder。我通常使用5个连接作为经验法则。
答案 11 :(得分:0)
StringBuilder 类为开发人员提供了一组方法,该方法允许操作可变的字符串,当您要修改字符串而不创建新对象时可以使用它,从而消除了普通String串联的开销或瓶颈问题。
在循环中将多个字符串连接在一起时,使用 StringBuilder 类可以提高性能。
下面是使用.NET中的StringBuilder类可以执行一些操作以操作字符串的操作的列表
附加:将信息附加到当前StringBuilder的末尾。
AppendFormat :用格式化文本替换字符串中传递的格式说明符。
插入:将字符串或对象插入当前StringBuilder的指定索引中。
删除:从当前的StringBuilder中删除指定数量的字符。
替换:在指定的索引处替换指定的字符。
答案 12 :(得分:0)
由于很难找到一个既不受观点影响又无自豪感的解释,我想在LINQpad上写一些代码来测试一下自己。
我发现使用小型字符串而不是使用i.ToString()会改变响应时间(在小循环中可见)。
该测试使用不同的迭代序列,以将时间测量值保持在合理的可比范围内。
我将在最后复制代码,以便您可以自己尝试(results.Charts ... Dump()在LINQPad之外不起作用)。
输出(X轴:测试的迭代次数,Y轴:以秒为单位的时间):
代码(使用LINQPad 5编写):
void Main()
{
Test(2, 3, 4, 5, 6, 7, 8, 9, 10);
Test(10, 20, 30, 40, 50, 60, 70, 80);
Test(100, 200, 300, 400, 500);
}
void Test(params int[] iterationsCounts)
{
$"Iterations sequence: {string.Join(", ", iterationsCounts)}".Dump();
int testStringLength = 10;
RandomStringGenerator.Setup(testStringLength);
var sw = new System.Diagnostics.Stopwatch();
var results = new Dictionary<int, TimeSpan[]>();
// This call before starting to measure time removes initial overhead from first measurement
RandomStringGenerator.GetRandomString();
foreach (var iterationsCount in iterationsCounts)
{
TimeSpan elapsedForString, elapsedForSb;
// string
sw.Restart();
var str = string.Empty;
for (int i = 0; i < iterationsCount; i++)
{
str += RandomStringGenerator.GetRandomString();
}
sw.Stop();
elapsedForString = sw.Elapsed;
// string builder
sw.Restart();
var sb = new StringBuilder(string.Empty);
for (int i = 0; i < iterationsCount; i++)
{
sb.Append(RandomStringGenerator.GetRandomString());
}
sw.Stop();
elapsedForSb = sw.Elapsed;
results.Add(iterationsCount, new TimeSpan[] { elapsedForString, elapsedForSb });
}
// Results
results.Chart(r => r.Key)
.AddYSeries(r => r.Value[0].Ticks, LINQPad.Util.SeriesType.Line, "String")
.AddYSeries(r => r.Value[1].Ticks, LINQPad.Util.SeriesType.Line, "String Builder")
.DumpInline();
}
static class RandomStringGenerator
{
static Random r;
static string[] strings;
public static void Setup(int testStringLength)
{
r = new Random(DateTime.Now.Millisecond);
strings = new string[10];
for (int i = 0; i < strings.Length; i++)
{
strings[i] = Guid.NewGuid().ToString().Substring(0, testStringLength);
}
}
public static string GetRandomString()
{
var indx = r.Next(0, strings.Length);
return strings[indx];
}
}