C# StringBuilder.Append 与 StringBuilder.AppendFormat
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/710504/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
StringBuilder.Append Vs StringBuilder.AppendFormat
提问by Sergio
I was wondering about StringBuilder and I've got a question that I was hoping the community would be able to explain.
我想知道 StringBuilder 并且我有一个问题,我希望社区能够解释。
Let's just forget about code readability, which of these is fasterand why?
让我们忘记代码可读性,哪些更快,为什么?
StringBuilder.Append
:
StringBuilder.Append
:
StringBuilder sb = new StringBuilder();
sb.Append(string1);
sb.Append("----");
sb.Append(string2);
StringBuilder.AppendFormat
:
StringBuilder.AppendFormat
:
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0}----{1}",string1,string2);
采纳答案by casperOne
It's impossible to say, not knowing the size of string1
and string2
.
这是不可能的说,不知道的大小string1
和string2
。
With the call to AppendFormat
, it will preallocate the buffer just once given the length of the format string and the strings that will be inserted and then concatenate everything and insert it into the buffer. For very large strings, this will be advantageous over separate calls to Append
which might cause the buffer to expand multiple times.
通过调用AppendFormat
,它会根据格式字符串的长度和将要插入的字符串预先分配一次缓冲区,然后连接所有内容并将其插入缓冲区。对于非常大的字符串,这将优于Append
可能导致缓冲区多次扩展的单独调用。
However, the three calls to Append
might or might not trigger growth of the buffer and that check is performed each call. If the strings are small enough and no buffer expansion is triggered, then it will be faster than the call to AppendFormat
because it won't have to parse the format string to figure out where to do the replacements.
但是,对三个调用Append
可能会也可能不会触发缓冲区的增长,并且每次调用都会执行该检查。如果字符串足够小并且没有触发缓冲区扩展,那么它会比调用 更快,AppendFormat
因为它不必解析格式字符串来确定在哪里进行替换。
More data is needed for a definitive answer
需要更多数据才能确定答案
It should be noted that there is little discussion of using the static Concat
method on the String
class(Jon's answerusing AppendWithCapacity
reminded me of this). His test results show that to be the best case (assuming you don't have to take advantage of specific format specifier). String.Concat
does the same thing in that it will predetermine the length of the strings to concatenate and preallocate the buffer (with slightly more overhead due to looping constructs through the parameters). It's performance is going to be comparable to Jon's AppendWithCapacity
method.
应该注意的是,几乎没有讨论Concat
在String
类上使用静态方法(Jon 的回答usingAppendWithCapacity
使我想起了这一点)。他的测试结果表明这是最好的情况(假设您不必利用特定的格式说明符)。 String.Concat
做同样的事情,因为它将预先确定要连接和预分配缓冲区的字符串的长度(由于通过参数循环构造,开销略高)。它的性能将与 Jon 的AppendWithCapacity
方法相媲美。
Or, just the plain addition operator, since it compiles to a call to String.Concat
anyways, with the caveat that all of the additions are in the same expression:
或者,只是简单的加法运算符,因为它编译为对任意的调用String.Concat
,但需要注意的是所有加法都在同一个表达式中:
// One call to String.Concat.
string result = a + b + c;
NOT
不是
// Two calls to String.Concat.
string result = a + b;
result = result + c;
For all those putting up test code
对于所有提交测试代码的人
You need to run your test cases in separateruns (or at the least, perform a GC between the measuring of separate test runs). The reason for this is that if you do say, 1,000,000 runs, creating a new StringBuilder
in each iteration of the loop for one test, and then you run the next test that loops the same number of times, creating an additional1,000,000 StringBuilder
instances, the GC will more than likely step in during the second test and hinder its timing.
您需要在单独的运行中运行测试用例(或者至少,在单独测试运行的测量之间执行 GC)。这样做的原因是,如果你说 1,000,000 次运行,StringBuilder
在循环的每次迭代中为一个测试创建一个新的,然后你运行下一个循环相同次数的测试,创建额外的1,000,000 个StringBuilder
实例,即 GC很可能会在第二次测试期间介入并阻碍其时机。
回答by Joel Coehoorn
Of course profile to know for sure in each case.
当然,在每种情况下都要确定要知道的配置文件。
That said, I think in general it will be the former because you aren't repeatedly parsing the format string.
也就是说,我认为通常是前者,因为您不会重复解析格式字符串。
However, the difference would be very small. To the point that you really should consider using AppendFormat
in most cases anyway.
但是,差异将非常小。AppendFormat
无论如何,您确实应该考虑在大多数情况下使用。
回答by Paul W Homer
I'd assume it was the call that did the least amount of work. Append just concatenates strings, where AppendFormat is doing string substitutions. Of course these days, you never can tell...
我认为是调用完成了最少的工作。Append 只是连接字符串,其中 AppendFormat 进行字符串替换。当然,这些天,你永远无法分辨......
回答by Micah
1 should be faster becuase it's simply appending the strings whereas 2 has to create a string based on a format and then append the string. So there's an extra step in there.
1 应该更快,因为它只是附加字符串,而 2 必须根据格式创建一个字符串,然后附加字符串。所以这里有一个额外的步骤。
回答by Andrew Hare
Append
will be faster in most cases because there are many overloads to that method that allow the compiler to call the correct method. Since you are using Strings
the StringBuilder
can use the String
overload for Append
.
Append
在大多数情况下会更快,因为该方法有许多重载,允许编译器调用正确的方法。由于您使用Strings
的StringBuilder
可使用String
的过载Append
。
AppendFormat
takes a String
and then an Object[]
which means that the format will have to be parsed and each Object
in the array will have to be ToString'd
before it can be added to the StringBuilder's
internal array.
AppendFormat
采用 aString
和 anObject[]
这意味着必须解析格式,并且必须先解析Object
数组中的每个格式,然后ToString'd
才能将其添加到StringBuilder's
内部数组中。
Note:To casperOne's point - it is difficult to give an exact answer without more data.
注意:对于 casperOne 的观点 - 如果没有更多数据,很难给出确切答案。
回答by Miha Markic
Faster is 1 in your case however it isn't a fair comparison. You should ask StringBuilder.AppendFormat()
vs StringBuilder.Append(string.Format())
- where the first one is faster due to internal working with char array.
在您的情况下,更快为 1,但这不是一个公平的比较。你应该问StringBuilder.AppendFormat()
vs StringBuilder.Append(string.Format())
- 由于内部使用 char 数组,第一个更快。
Your second option is more readable though.
不过,您的第二个选项更具可读性。
回答by John Rasch
casperOne is correct. Once you reach a certain threshold, the Append()
method becomes slower than AppendFormat()
. Here are the different lengths and elapsed ticks of 100,000 iterations of each method:
casperOne 是正确的。一旦达到某个阈值,该Append()
方法就会变得比 慢AppendFormat()
。以下是每种方法 100,000 次迭代的不同长度和经过的滴答声:
Length: 1
长度:1
Append() - 50900
AppendFormat() - 126826
Length: 1000
长度:1000
Append() - 1241938
AppendFormat() - 1337396
Length: 10,000
长度:10,000
Append() - 12482051
AppendFormat() - 12740862
Length: 20,000
长度:20,000
Append() - 61029875
AppendFormat() - 60483914
When strings with a length near 20,000 are introduced, the AppendFormat()
function will slightlyoutperform Append()
.
当引入近20,000长度的字符串,该AppendFormat()
函数将小幅跑赢大盘Append()
。
Why does this happen? See casperOne's answer.
为什么会发生这种情况?请参阅casperOne 的回答。
Edit:
编辑:
I reran each test individually under Release configuration and updated the results.
我在发布配置下单独重新运行每个测试并更新结果。
回答by Jon Skeet
casperOne is entirely accurate that it depends on the data. However, suppose you're writing this as a class library for 3rd parties to consume - which would you use?
casperOne 完全准确,它取决于数据。但是,假设您将其编写为供 3rd 方使用的类库 - 您会使用哪个?
One option would be to get the best of both worlds - work out how much data you're actually going to have to append, and then use StringBuilder.EnsureCapacityto make sure we only need a single buffer resize.
一种选择是两全其美 - 计算出您实际需要追加的数据量,然后使用StringBuilder.EnsureCapacity确保我们只需要调整单个缓冲区大小。
If I weren't toobothered though, I'd use Append
x3 - it seems "more likely" to be faster, as parsing the string format tokens on every call is clearly make-work.
如果我不太烦恼,我会使用Append
x3 - 它似乎“更有可能”更快,因为在每次调用时解析字符串格式标记显然是可行的。
Note that I've asked the BCL team for a sort of "cached formatter" which we could create using a format string and then re-use repeatedly. It's crazy that the framework has to parse the format string each time it's used.
请注意,我已经向 BCL 团队询问了一种“缓存格式化程序”,我们可以使用格式字符串创建它,然后重复使用。框架每次使用时都必须解析格式字符串,这很疯狂。
EDIT: Okay, I've edited John's code somewhat for flexibility and added an "AppendWithCapacity" which just works out the necessary capacity first. Here are the results for the different lengths - for length 1 I used 1,000,000 iterations; for all other lengths I used 100,000. (This was just to get sensible running times.) All times are in millis.
编辑:好的,为了灵活性,我编辑了 John 的代码,并添加了一个“AppendWithCapacity”,它首先计算出必要的容量。以下是不同长度的结果 - 对于长度 1,我使用了 1,000,000 次迭代;对于所有其他长度,我使用了 100,000。(这只是为了获得合理的运行时间。)所有时间都以毫秒为单位。
Unfortunately tables don't really work in SO. The lengths were 1, 1000, 10000, 20000
不幸的是,表格在 SO 中并不真正起作用。长度为 1, 1000, 10000, 20000
Times:
次数:
- Append: 162, 475, 7997, 17970
- AppendFormat: 392, 499, 8541, 18993
- AppendWithCapacity: 139, 189, 1558, 3085
- 附加:162、475、7997、17970
- 附加格式:392、499、8541、18993
- 附加容量:139、189、1558、3085
So as it happened, I never saw AppendFormat beat Append - but I didsee AppendWithCapacity win by a very substantial margin.
因此,碰巧的是,我从未见过 AppendFormat 击败 Append - 但我确实看到 AppendWithCapacity 以非常可观的优势获胜。
Here's the full code:
这是完整的代码:
using System;
using System.Diagnostics;
using System.Text;
public class StringBuilderTest
{
static void Append(string string1, string string2)
{
StringBuilder sb = new StringBuilder();
sb.Append(string1);
sb.Append("----");
sb.Append(string2);
}
static void AppendWithCapacity(string string1, string string2)
{
int capacity = string1.Length + string2.Length + 4;
StringBuilder sb = new StringBuilder(capacity);
sb.Append(string1);
sb.Append("----");
sb.Append(string2);
}
static void AppendFormat(string string1, string string2)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0}----{1}", string1, string2);
}
static void Main(string[] args)
{
int size = int.Parse(args[0]);
int iterations = int.Parse(args[1]);
string method = args[2];
Action<string,string> action;
switch (method)
{
case "Append": action = Append; break;
case "AppendWithCapacity": action = AppendWithCapacity; break;
case "AppendFormat": action = AppendFormat; break;
default: throw new ArgumentException();
}
string string1 = new string('x', size);
string string2 = new string('y', size);
// Make sure it's JITted
action(string1, string2);
GC.Collect();
Stopwatch sw = Stopwatch.StartNew();
for (int i=0; i < iterations; i++)
{
action(string1, string2);
}
sw.Stop();
Console.WriteLine("Time: {0}ms", (int) sw.ElapsedMilliseconds);
}
}
回答by Tommy Carlier
StringBuilder
also has cascaded appends: Append()
returns the StringBuilder
itself, so you can write your code like this:
StringBuilder
也有级联附加:Append()
返回StringBuilder
本身,因此您可以像这样编写代码:
StringBuilder sb = new StringBuilder();
sb.Append(string1)
.Append("----")
.Append(string2);
Clean, and it generates less IL-code (although that's really a micro-optimization).
干净,它生成更少的 IL 代码(尽管这确实是一个微优化)。