Skip to content

Of course. This time, we'll talk about a classic "anti-pattern," a fundamental performance mistake in Dart and many other languages: string concatenation in a loop.


## Dart Optimization Series

## Chapter 3: The "Garbage Storm" from Joining Strings

The Scenario 📝

  • The System: A Dart application needs to build a large string from many smaller pieces, like creating a CSV file, a JSON payload, or an HTML report.
  • The Problem: The app becomes unusually slow and uses a lot of memory when processing a large list of items.

The Problematic Code (Using the + operator)

dart
String createCsv(List<User> users) {
  String csv = 'id,name,email\n';

  // MISTAKE: Using `+=` inside a loop
  for (final user in users) {
    csv += '${user.id},${user.name},${user.email}\n';
  }

  return csv;
}

Analyzing the Bottleneck: Strings are Immutable 🧐

  1. The Core Rule: In Dart, String objects are immutable. This means once a string is created, it can never be changed.
  2. What Really Happens with +=?:
    • The line csv += ... doesn't just "add" to the old string. Instead, on every single loop:
      1. It creates a brand new String object in memory.
      2. It copies the entire content of the old csv string to the new one.
      3. It adds the current user's string to the end.
      4. The old String object becomes garbage.
  3. The Consequence: If you have a list of 10,000 users, you will create 10,000 temporary String objects that are immediately thrown away. This creates a huge "garbage storm," putting a lot of pressure on the Garbage Collector (GC), which uses up CPU and slows down your app.

The Solution: Use StringBuffer

  • The Logic: StringBuffer is a class designed specifically for building strings efficiently. It uses a mutable (changeable) internal buffer.

  • The Optimized Code:

    dart
    String createCsv_Optimized(List<User> users) {
      final buffer = StringBuffer();
    
      buffer.writeln('id,name,email'); // Add the header line
    
      for (final user in users) {
        // .write() and .writeln() just add to the buffer, no new objects are made
        buffer.writeln('${user.id},${user.name},${user.email}');
      }
    
      // Only ONE String object is created at the very end
      return buffer.toString();
    }

Analyzing the Results

  1. Less Memory Allocation: Instead of 10,001 objects, you only create 1 StringBuffer and 1 final String.
  2. Less GC Pressure: Almost no garbage is created, which helps your app run smoothly.
  3. Faster Speed: By eliminating thousands of memory allocation and copy operations, the code runs many times faster.

Conclusion:

  • The Golden Rule: "Never use + or += to join strings inside a loop."
  • Always use StringBuffer when you need to build a string from multiple parts, especially when the number of parts is large or unknown.