Предположим, у меня есть построитель строк на C#, который делает это:
StringBuilder sb = new StringBuilder();
string cat = "cat";
sb.Append("the ").Append(cat).(" in the hat");
string s = sb.ToString();
будет ли это так же эффективно или даже более эффективно, чем иметь:
string cat = "cat";
string s = String.Format("The {0} in the hat", cat);
Если да, то почему?
EDIT
После нескольких интересных ответов я понял, что, возможно, мне следовало быть немного яснее в том, о чем я спрашивал. Я спрашивал не столько о том, кто быстрее конкатенирует строку, сколько о том, кто быстрее инжектирует одну строку в другую.
В обоих приведенных выше случаях я хочу вставить одну или несколько строк в середину предопределенной строки шаблона.
Извините за путаницу
ПРИМЕЧАНИЕ: Этот ответ был написан, когда.NET 2.0 был текущей версией. Это больше может не относиться к более поздним версиям.
'Последовательность. Формат' использует 'StringBuilder' внутренне:
public static string Format(IFormatProvider provider, string format, params object[] args)
{
if ((format == null) || (args == null))
{
throw new ArgumentNullException((format == null) ? "format" : "args");
}
StringBuilder builder = new StringBuilder(format.Length + (args.Length * 8));
builder.AppendFormat(provider, format, args);
return builder.ToString();
}
Вышеупомянутый кодекс - отрывок от mscorlib, таким образом, вопрос становится " 'StringBuilder. Приложите ()' быстрее, чем 'StringBuilder. AppendFormat () '"?
Не определяя эффективность I' d, вероятно, говорят, что кодовый образец выше управлял бы более быстро использованием '.Append ()'. Но it' s предположение, попытайтесь определить эффективность и/или представить два, чтобы получить надлежащее сравнение.
Этот парень, Джерри Диксон, сделал некоторый сопоставительный анализ:
Обновленный:
Печально связь выше с тех пор умерла. Однако there' s все еще копия на Пути Обратная Машина:
В конце дня это зависит, будет ли Ваше форматирование последовательности названным повторяющимся образом, т.е. you' ре, переделывающее некоторую серьезную текстовую обработку 100' s мегабайтов текста, или ли it' s быть названным, когда пользователь нажимает кнопку время от времени. Если you' ре, делающее некоторую огромную работу пакетной обработки данных I' d придерживаются Последовательности. Формат, это помогает кодовой удобочитаемости. Если Вы подозреваете, что узкое место перфекта тогда прикрепляет профилировщика на Ваш кодекс и видит, где это действительно.
Производительность операции конкатенации для объекта String или StringBuilder зависит от того, как часто происходит выделение памяти. Операция конкатенации String всегда выделяет память, тогда как операция конкатенации StringBuilder выделяет память, только если буфер объекта StringBuilder слишком мал для размещения новых данных. Следовательно, класс String предпочтительнее для операции конкатенации, если конкатенируется фиксированное количество объектов String. В этом случае отдельные операции конкатенации могут быть даже объединены компилятором в одну операцию. Объект StringBuilder предпочтителен для операции конкатенации, если конкатенируется произвольное количество строк; например, если цикл конкатенирует случайное количество строк пользовательского ввода.
Я управлял некоторыми быстрыми исполнительными оценками, и для 100 000 операций составил в среднем более чем 10 пробегов, первый метод (Строитель Последовательности) берет почти половину времени второго (Формат Последовательности).
Так, если это нечастое, это doesn' t вопрос. Но если это - общая операция, тогда Вы можете хотеть использовать первый метод.
Я ожидал бы Последовательность. Формат , чтобы быть медленнее - это должно разобрать последовательность, и тогда связывают его.
Несколько примечаний:
Если только потому, что последовательность. Формат doesn' t точно делают то, что Вы могли бы думать, вот является повторный показ тестов 6 годами позже Net45.
Concat является все еще самым быстрым, но действительно it' s меньше чем 30%-е различие. StringBuilder и Формат отличаются едва на 5-10%. Я получил изменения 20%, запускающих тесты несколько раз.
Миллисекунды, миллион повторений:
Урок, который я устраняю, - то, что разница в результативности тривиальна и так это shouldn' t останавливают Вас пишущий самый простой удобочитаемый кодекс, Вы можете. Который за мои деньги является часто, но не всегда '+ b + c'.
const int iterations=1000000;
var keyprefix= this.GetType().FullName;
var maxkeylength=keyprefix + 1 + 1+ Math.Log10(iterations);
Console.WriteLine("KeyPrefix \"{0}\", Max Key Length {1}",keyprefix, maxkeylength);
var concatkeys= new string[iterations];
var stringbuilderkeys= new string[iterations];
var cachedsbkeys= new string[iterations];
var formatkeys= new string[iterations];
var stopwatch= new System.Diagnostics.Stopwatch();
Console.WriteLine("Concatenation:");
stopwatch.Start();
for(int i=0; i<iterations; i++){
var key1= keyprefix+":" + i.ToString();
concatkeys[i]=key1;
}
Console.WriteLine(stopwatch.ElapsedMilliseconds);
Console.WriteLine("New stringBuilder for each key:");
stopwatch.Restart();
for(int i=0; i<iterations; i++){
var key2= new StringBuilder(keyprefix).Append(":").Append(i.ToString()).ToString();
stringbuilderkeys[i]= key2;
}
Console.WriteLine(stopwatch.ElapsedMilliseconds);
Console.WriteLine("Cached StringBuilder:");
var cachedSB= new StringBuilder(maxkeylength);
stopwatch.Restart();
for(int i=0; i<iterations; i++){
var key2b= cachedSB.Clear().Append(keyprefix).Append(":").Append(i.ToString()).ToString();
cachedsbkeys[i]= key2b;
}
Console.WriteLine(stopwatch.ElapsedMilliseconds);
Console.WriteLine("string.Format");
stopwatch.Restart();
for(int i=0; i<iterations; i++){
var key3= string.Format("{0}:{1}", keyprefix,i.ToString());
formatkeys[i]= key3;
}
Console.WriteLine(stopwatch.ElapsedMilliseconds);
var referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway= concatkeys.Union(stringbuilderkeys).Union(cachedsbkeys).Union(formatkeys).LastOrDefault(x=>x[1]=='-');
Console.WriteLine(referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway);
Я думаю в большинстве случаев как эта ясность, и не эффективность, должно быть Ваше самое большое беспокойство. Если you' ре, сокрушительное вместе тонны последовательностей или строительство чего-то для более низкого приведенного в действие мобильного устройства, это, вероятно, won' t делают большую часть вмятины в Вашей скорости пробега.
I' ve нашел что в случаях где I' m строящие последовательности довольно линейным способом, или выполнение прямых связей или использование StringBuilder являются Вашим наилучшим вариантом. Я предлагаю это в случаях где большинство последовательности это you' здание ре динамичное. Так как очень мало текста статично, самая важная вещь - это it' s ясный, куда каждая часть динамического текста помещается в случае, если этому нужно обновленный в будущем.
С другой стороны, если you' ре, говорящее о большой части статического текста с двумя или тремя переменными в нем, даже если it' s немного менее эффективный, я думаю ясность, которую Вы получаете от последовательности. Формат делает его стоящим того. Я использовал это ранее на этой неделе, имея необходимость поместить один бит динамического текста в центре документа на 4 страницы. It' ll быть легче обновить ту большую часть текста, если в одной части, чем необходимость обновить три части, которые Вы связываете вместе.
Последовательность. Формат использует 'StringBuilder' внутренне... настолько логически, который приводит к идее, что это было бы немного менее производительно из-за более верхнего. Однако простая связь последовательности - самый быстрый метод впрыскивания одной последовательности между двумя другими... существенной степенью. Эти доказательства были продемонстрированы Рико Мэриэни в его самой первой Исполнительной Викторине, несколько лет назад. Очевидный факт - то, что связи..., когда количество частей последовательности известно (без ограничения.. Вы могли связать тысячу частей..., пока Вы всегда знаете 1 000 частей)... всегда быстрее, чем 'StringBuilder' или String. Формат. Они могут быть выполнены с единственным распределением памяти ряд копий памяти. Здесь доказательство
И вот фактический кодекс для некоторой Последовательности. Методы Concat, которые в конечном счете называют FillStringChecked, который использует указатели, чтобы скопировать память (извлеченный через Отражатель):
public static string Concat(params string[] values)
{
int totalLength = 0;
if (values == null)
{
throw new ArgumentNullException("values");
}
string[] strArray = new string[values.Length];
for (int i = 0; i < values.Length; i++)
{
string str = values[i];
strArray[i] = (str == null) ? Empty : str;
totalLength += strArray[i].Length;
if (totalLength < 0)
{
throw new OutOfMemoryException();
}
}
return ConcatArray(strArray, totalLength);
}
public static string Concat(string str0, string str1, string str2, string str3)
{
if (((str0 == null) && (str1 == null)) && ((str2 == null) && (str3 == null)))
{
return Empty;
}
if (str0 == null)
{
str0 = Empty;
}
if (str1 == null)
{
str1 = Empty;
}
if (str2 == null)
{
str2 = Empty;
}
if (str3 == null)
{
str3 = Empty;
}
int length = ((str0.Length + str1.Length) + str2.Length) + str3.Length;
string dest = FastAllocateString(length);
FillStringChecked(dest, 0, str0);
FillStringChecked(dest, str0.Length, str1);
FillStringChecked(dest, str0.Length + str1.Length, str2);
FillStringChecked(dest, (str0.Length + str1.Length) + str2.Length, str3);
return dest;
}
private static string ConcatArray(string[] values, int totalLength)
{
string dest = FastAllocateString(totalLength);
int destPos = 0;
for (int i = 0; i < values.Length; i++)
{
FillStringChecked(dest, destPos, values[i]);
destPos += values[i].Length;
}
return dest;
}
private static unsafe void FillStringChecked(string dest, int destPos, string src)
{
int length = src.Length;
if (length > (dest.Length - destPos))
{
throw new IndexOutOfRangeException();
}
fixed (char* chRef = &dest.m_firstChar)
{
fixed (char* chRef2 = &src.m_firstChar)
{
wstrcpy(chRef + destPos, chRef2, length);
}
}
}
Таким образом:
string what = "cat";
string inthehat = "The " + what + " in the hat!";
Наслаждайтесь!
В обоих случаях выше я хочу ввести одну или несколько последовательностей в середину предопределенной последовательности шаблона.
В этом случае я предложил бы Последовательность. Формат является самым быстрым, потому что это - дизайн для той точной цели.
Это действительно зависит от ситуации. Для небольших строк с небольшим количеством конкатенаций, на самом деле, быстрее просто добавить строки.
String s = "String A" + "String B";
Но для больших строк (очень больших строк) эффективнее использовать StringBuilder.
Это действительно зависит от Вашего образца использования.
Подробная оценка между 'последовательностью. Соединение', 'последовательность, Concat' и 'последовательность. Формат' может быть найден здесь: Последовательность. Формат Isn' t Подходящий для Интенсивной Регистрации
Я бы предположил, что нет, поскольку String.Format не предназначен для конкатенации, он был разработан для форматирования вывода различных входных данных, таких как дата.
String s = String.Format("Today is {0:dd-MMM-yyyy}.", DateTime.Today);