String을 여러 번 수정해야할 때, StringBuilder를 쓰는 것이 효율적이다. 그 이유를 정리한다.
String Builder는 문자열을 변경하는 용도로 사용할 수 있는 Class이다.
using System.Text;
// String Builder
var currentWorkingSet = Environment.WorkingSet;
var current = Environment.TickCount;
StringBuilder sb = new();
for (int i = 0; i < 10000; i++)
{
sb.Append(i.ToString());
}
var sbString = sb.ToString();
Console.WriteLine($"String builder: {Environment.TickCount - current} milliseconds");
Console.WriteLine(Environment.WorkingSet - currentWorkingSet);
// String Adding
currentWorkingSet = Environment.WorkingSet;
current = Environment.TickCount;
string str = "";
for (int i = 0; i < 10000; i++)
{
str += i.ToString();
}
Console.WriteLine($"String Add: {Environment.TickCount - current} milliseconds");
Console.WriteLine(Environment.WorkingSet - currentWorkingSet);
Console.WriteLine($"Is same string: {sbString.Equals(str)}");
// Outputs
// String builder: 1 milliseconds
// 2744320
// String Add: 33 milliseconds
// 4407296
// Is same string: True
두 방식은 시간과 메모리 사용량 모두 차이를 보인다. String
은 값 타입이며, 값 타입은 값을 수정할 시에 새로 데이터를 할당한다. String을 반복해서 더하는 방식은 매번 String 타입의 데이터를 생성한다.
StringChecker();
unsafe void StringChecker()
{
string value = "first";
fixed (char* start = value)
{
value = String.Concat(value, "Second");
fixed (char* current = value)
{
Console.WriteLine($"Concat result: {start == current}");
}
}
fixed (char* start = value)
{
value += "Third";
fixed (char* current = value)
{
Console.WriteLine($"Adding result: {start == current}");
}
}
}
// Output
// Concat result: False
// Adding result: False
- Tip: unsafe를 run하기 위해선 .csproj에 아래 구문을 추가해주어야 함.
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
StringBuilder는 이러한 문제를 방지하고 반복문 안에서 효과적으로 string 연산을 수행할 수 있다.
기본으로 16자를 용량으로 가지고 있으며, 용량을 초과할 시 List와 같은 방식으로 2배씩 늘어난다. 최초에 초기 용량을 지정할 수 있으며, 배수하여 늘어나는 최대 길이는 2 billion(21억) 정도다.