百万级字符串批量替换指定字符的高效实现方案咨询
Great question—when you're dealing with million-character strings, those repeated Replace calls are a total performance killer. The problem is that C# strings are immutable—every time you call Replace, it creates an entirely new string in memory. Doing this three times (once for each character) means you're traversing the entire string three times and allocating three separate large strings, which craters both speed and memory efficiency.
Let's look at two far better approaches that only traverse the string once, with minimal memory overhead:
1. Using StringBuilder with a HashSet<char> (Balanced Speed & Readability)
This is the most straightforward optimized approach. StringBuilder lets us build the result in-place without creating intermediate strings, and HashSet<char> gives us O(1) lookups to check if a character needs removal (way faster than checking an array with Contains, which is O(k) where k is the number of chars to remove).
string input = "xeatq"; // Replace with your million-length string HashSet<char> charsToRemove = new HashSet<char> { 'z', 'x', 'q' }; // Preallocate the StringBuilder to match the input length to avoid resizing StringBuilder sb = new StringBuilder(input.Length); foreach (char c in input) { if (!charsToRemove.Contains(c)) { sb.Append(c); } } return sb.ToString();
Why this works:
- We only iterate over the input string once, not three times.
- Preallocating the
StringBuildercapacity eliminates costly resizing operations as we build the result. HashSetmakes checking if a character should be removed nearly instant, even if you add more characters to the removal list later.
2. Reusing a char[] (Maximum Memory Efficiency)
If you want to squeeze out every bit of performance and minimize memory allocations, you can directly manipulate a character array. This avoids the small overhead of StringBuilder and reuses the array created by ToCharArray():
string input = "xeatq"; HashSet<char> charsToRemove = new HashSet<char> { 'z', 'x', 'q' }; char[] charArray = input.ToCharArray(); int resultIndex = 0; foreach (char c in charArray) { if (!charsToRemove.Contains(c)) { charArray[resultIndex++] = c; } } // Create a new string from the first `resultIndex` characters of the array return new string(charArray, 0, resultIndex);
Why this works:
- We reuse the existing character array instead of allocating a separate one for
StringBuilder. - No intermediate objects are created during the iteration—we just overwrite the array in place with valid characters.
- The final string creation only uses the portion of the array that contains valid characters, so no wasted memory.
Performance Comparison
Your original approach:
- Time complexity: O(3n) (3 full traversals of the input string)
- Memory: 3x the input size (three separate string allocations)
Optimized approaches:
- Time complexity: O(n) (single traversal)
- Memory: ~1x the input size (one allocation for the result)
For million-length strings, this will be orders of magnitude faster and drastically reduce GC pressure.
内容的提问来源于stack exchange,提问作者Dzarrah Developer

