C# & .NET Framework Arrays & String Mastery
💡
Exercise 29

StringBuilder — The Efficient Report Builder 20 XP Medium

Ctrl+Enter Run Ctrl+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.
  • Key methods: Append, AppendLine, Insert, Remove, Replace, ToString.
  • 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.
main.py
Hi! I'm Rex 👋
Output
Ready. Press ▶ Run or Ctrl+Enter.