StringBuilder — The Efficient Report Builder
20 XPMedium
Ctrl+Enter RunCtrl+S Save
📋 Building Reports Efficiently
The architect's office generates hundreds of construction reports daily — progress updates, inspection logs, material inventories. Assembling these reports by concatenating strings with + in a loop is like rewriting the entire document from scratch every time you add a single sentence. For small labels, that's fine. For large reports? It's a performance disaster.
This is where System.Text.StringBuilder enters the scene — a mutable string buffer that lets you append, insert, remove, and replace characters without creating a new object each time.
🐌 The Problem with String Concatenation
Since strings are immutable in .NET, every + operation creates a brand new string on the heap. In a loop, this means:
// DON'T DO THIS for large loops!
string report = "";
for (int i = 0; i < 10000; i++)
{
report += $"Line {i}\n"; // Creates a NEW string every iteration!
}
// This creates ~10,000 intermediate string objects
// Time complexity: O(n²) because each += copies the entire string
Each += allocates a new string of increasing size and copies all previous characters into it. For n iterations, that's roughly O(n²) total characters copied. With 10,000 lines, you're copying millions of characters and creating thousands of garbage objects for the GC.
🚀 Enter StringBuilder
StringBuilder maintains an internal char[] buffer that grows as needed (typically doubling in capacity). Appending text is an O(1) amortized operation — it just copies the new characters into the buffer without reallocating the entire contents.
using System.Text;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
sb.AppendLine($"Line {i}"); // Appends in-place, no new objects!
}
string report = sb.ToString(); // Convert to string once at the end
// Time complexity: O(n) — each append is O(1) amortized
🛠️ StringBuilder Methods
StringBuilder provides a rich set of mutation methods, all of which modify the buffer in place and return the same StringBuilder instance (enabling method chaining).
using System.Text;
StringBuilder sb = new StringBuilder();
// Append — add text to the end
sb.Append("Project: ");
sb.AppendLine("Skyline Tower"); // Adds text + newline
// Insert — add text at a specific index
sb.Insert(0, "=== REPORT ===\n");
// Replace — swap text in-place
sb.Replace("Skyline", "Horizon");
// Remove — delete a range of characters
// sb.Remove(startIndex, length);
// ToString — get the final string
string result = sb.ToString();
Console.WriteLine(result);
📏 Capacity and Length
StringBuilder has two important size properties:
Length — the number of characters currently in the buffer (the actual content size).
Capacity — the size of the internal buffer (how much it can hold before resizing).
When Length exceeds Capacity, the buffer automatically doubles in size.
You can set an initial capacity in the constructor to avoid repeated resizing: new StringBuilder(1024).
Setting Length = 0 clears the content without deallocating the buffer — useful for reuse.
using System.Text;
StringBuilder sb = new StringBuilder(256); // Pre-allocate 256 chars
Console.WriteLine(sb.Capacity); // 256
Console.WriteLine(sb.Length); // 0
sb.Append("Hello, Architect!");
Console.WriteLine(sb.Length); // 17
Console.WriteLine(sb.Capacity); // 256 (still fits)
// Clear and reuse
sb.Length = 0;
// or sb.Clear(); // Same effect, more readable
🏎️ Performance Comparison
using System;
using System.Diagnostics;
using System.Text;
int iterations = 50000;
// String concatenation
Stopwatch sw1 = Stopwatch.StartNew();
string s = "";
for (int i = 0; i < iterations; i++)
s += "x";
sw1.Stop();
// StringBuilder
Stopwatch sw2 = Stopwatch.StartNew();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < iterations; i++)
sb.Append("x");
string result = sb.ToString();
sw2.Stop();
Console.WriteLine($"String: {sw1.ElapsedMilliseconds}ms");
Console.WriteLine($"StringBuilder: {sw2.ElapsedMilliseconds}ms");
// Typical result: String ~2000ms, StringBuilder ~1ms
📝 When to Use What
Use string + for simple, one-time concatenation of a few values (the compiler sometimes optimizes this to String.Concat).
Use $"..." interpolation for readable formatting of a few variables.
Use StringBuilder when building strings in a loop, especially with unknown or large iteration counts.
Rule of thumb: If you're concatenating in a loop, always use StringBuilder.
Use String.Join when combining an array/collection with a separator — it's optimized internally.
⚡ Key Takeaways
Strings are immutable; += in a loop creates O(n²) work and n garbage objects.
StringBuilder uses a mutable internal buffer with O(1) amortized append.
Pre-allocate capacity with new StringBuilder(capacity) to avoid resizing.
Always call .ToString() at the end to get your final string.
📋 Instructions
## 📋 Construction Report Builder Challenge
The architect needs a construction progress report built efficiently.
1. Add `using System.Text;` at the top
2. Create a `StringBuilder` called `report`
3. Append these 5 lines using `AppendLine()`:
- `"=== Construction Report ==="`
- `"Foundation: Complete"`
- `"Framing: In Progress"`
- `"Electrical: Pending"`
- `"Plumbing: Pending"`
4. Print `"Report generated!"`
5. Count the lines: Split the `report.ToString()` by `'\n'` and count non-empty entries. Print `"Lines: 5"`
6. Print the total character count of the report (use `report.Length`): `"Length: 89"`
7. Print `"String concat: slow for large data"`
8. Print `"StringBuilder: fast and efficient!"`
Create with `StringBuilder report = new StringBuilder();` then call `report.AppendLine("...")` for each line. For counting lines, use `report.ToString().Split('\n', StringSplitOptions.RemoveEmptyEntries).Length`. The `report.Length` property gives the total character count. Print the performance notes as literal strings.