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 🧐
- The Core Rule: In Dart,
Stringobjects are immutable. This means once a string is created, it can never be changed. - What Really Happens with
+=?:- The line
csv += ...doesn't just "add" to the old string. Instead, on every single loop:- It creates a brand new
Stringobject in memory. - It copies the entire content of the old
csvstring to the new one. - It adds the current
user's string to the end. - The old
Stringobject becomes garbage.
- It creates a brand new
- The line
- The Consequence: If you have a list of 10,000 users, you will create 10,000 temporary
Stringobjects 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:
StringBufferis a class designed specifically for building strings efficiently. It uses a mutable (changeable) internal buffer.The Optimized Code:
dartString 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 ✨
- Less Memory Allocation: Instead of 10,001 objects, you only create 1
StringBufferand 1 finalString. - Less GC Pressure: Almost no garbage is created, which helps your app run smoothly.
- 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
StringBufferwhen you need to build a string from multiple parts, especially when the number of parts is large or unknown.