Я понимаю преимущества StringBuilder.
Но если я хочу конкатенировать 2 строки, то я предполагаю, что лучше (быстрее) сделать это без StringBuilder. Правильно ли это?
В какой момент (количество строк) становится лучше использовать StringBuilder?
Я понимаю преимущества StringBuilder.
Но если я хочу конкатенировать 2 строки, то я предполагаю, что лучше (быстрее) сделать это без StringBuilder. Правильно ли это?
В какой момент (количество строк) становится лучше использовать StringBuilder?
Я тепло предлагаю вам прочитать The Sad Tragedy of the Micro-Optimization Theatre от Джеффа Этвуда.
Он обрабатывает Simple Concatenation vs. StringBuilder и другие методы.
Теперь, если вы хотите увидеть некоторые цифры и графики, перейдите по ссылке;)
Но если я хочу объединить 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, когда вы объединяете в нетривиальном петля - особенно если вы не знаете наверняка (во время компиляции) сколько итерации, которые вы будете делать через петля. Например, чтение файла характер за один раз, создавая Строка, как вы идете, используя оператор + = потенциально самоубийство производительности.
Обязательно используйте конкатенацию оператор, когда вы можете (читаемо) указать все, что должно быть объединены в одном заявлении. (Если ты иметь множество вещей, чтобы объединить, рассмотреть вопрос о звонке String.Concat явно - или String.Join, если вам нужен разделитель.)
Не бойтесь разбить литералы на несколько сцепленных битов - результат будет таким же. Вы можете помочь удобочитаемость, ломая длинный литерал в несколько строк, например, с без ущерба для производительности.
Если вам нужны промежуточные результаты конкатенации чего-то кроме подачи следующей итерации конкатенации, StringBuilder не является собираюсь помочь тебе. Например, если вы создаете полное имя из первого имя и фамилию, а затем добавить третья часть информации ( прозвище, может быть) до конца только выгода от использования StringBuilder если вам не нужно (имя + фамилия) строка для других целей (как мы делаем в примере, который создает объект Person).
Если у вас есть несколько объединений делать, и вы действительно хотите сделать их в отдельных заявлениях, это не действительно важно, каким путем вы идете. Который Более эффективный способ будет зависеть от количество конкатенаций размеров строки и в каком порядке они соединены. Если вы действительно считаю, что кусок кода, чтобы быть узкое место, профиль или сравните это в обоих направлениях.
System.String - неизменяемый объект - это означает, что всякий раз, когда вы изменяете его содержимое, он выделяет новую строку, и для этого требуется время (и память?). Используя StringBuilder, вы изменяете фактическое содержимое объекта без выделения нового.
Поэтому используйте StringBuilder, когда вам нужно сделать много изменений в строке.
Не совсем... вы должны использовать StringBuilder, если вы объединяете большие строки или у вас много конкатенаций, например, в цикле.
Вот простое тестовое приложение, чтобы доказать точку:
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'000 итераций
1000 итераций
500 итераций
Но если я хочу объединить 2 строки, то я предполагаю, что лучше и быстрее сделать это без StringBuilder. Это правильно?
Да. Но что более важно, в таких ситуациях гораздо удобнее использовать ваниль String
. С другой стороны, использование его в цикле имеет смысл, а также может быть таким же удобным для чтения, как и конкатенация.
Я бы с осторожностью следил за эмпирическими правилами, в которых в качестве порогового значения приводятся конкретные числа конкатенации. Использование его в циклах (и только в циклах), вероятно, столь же полезно, легче запомнить и имеет больше смысла.
Нет окончательного ответа, только правила большого пальца. Мои личные правила выходят примерно так:
StringBuilder
.StringBuilder
.StringBuilder
.Перефразировать
Тогда будешь считать до трех, не больше, не меньше. Три - это число, которое вы должны подсчитать, а число подсчетов должно быть три. Четыре не засчитываешь, и не считай двух, кроме того, что ты переходишь к трем. Как только число три, будучи третьим номером, достигнем, тогда проглотите свою Святую Ручную гранату Антиохии
Я обычно использую строковый построитель для любого блока кода, который приведет к конкатенации трех или более строк.
Пока вы можете физически вводить количество конкатенаций (a + b + c...), это не должно иметь большого значения. Квадрат N (при N = 10) представляет собой 100-кратное замедление, которое не должно быть слишком плохим.
Большая проблема заключается в том, что вы объединяете сотни строк. При N = 100 вы получаете замедление в 10000 раз. Это довольно плохо.
Я не думаю, что существует тонкая грань между тем, когда использовать или когда этого не делать. Если, конечно, кто-то не выполнил некоторые обширные испытания, чтобы выйти с золотыми условиями.
Для меня я не буду использовать StringBuilder, если просто конкатенировать 2 огромные строки. Если цикл с неопределенным подсчетом, я, скорее всего, даже если цикл может быть небольшим.
Одиночная конкатенация не стоит использовать StringBuilder. Я, как правило, использовал 5 конкатенаций.
Поскольку было трудно найти объяснение этому, на которое бы не влияли мнения или не последовало сражение гордости, я решил написать на LINQpad немного кода, чтобы проверить это сам.
Я обнаружил, что использование строк небольшого размера вместо использования i.ToString() изменяет время отклика (отображается в маленьких циклах).
В тесте используются разные последовательности итераций для измерения времени в разумно сопоставимых диапазонах.
В конце я скопирую код, чтобы вы могли попробовать его самостоятельно (results.Charts... Dump() не будет работать вне LINQPad).
Выходные данные (ось Y: количество проверенных итераций, ось X: отдельные тесты):
Последовательность итераций: 2, 3, 4, 5, 6, 7, 8, 9, 10
Последовательность итераций: 10, 20, 30, 40, 50, 60, 70, 80
Последовательность итераций: 100, 200, 300, 400, 500
Код (написанный с использованием 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];
}
}