String
for
-loopThe "for" keyword can loop over characters in strings. But some code examples use an inefficient pattern. This causes unnecessary allocations on the managed heap.
By directly accessing characters, not strings, loops can be improved and simplified. Many programs can be improved.
Consider a string
like "Dot Net Perls website" with spaces between words. We want to loop over each the string
, in an efficient way, counting spaces.
Input = Dot Net Perls website Spaces = 3
To begin, we see the optimal looping code. We use a for
-loop to iterate over a string
, and we do not incur any excess allocations.
using System; class Program { public static void Main() { string input = "Dot Net Perls website"; int spaces = 0; for (int i = 0; i < input.Length; i++) { if (input[i] == ' ') { spaces++; } } Console.WriteLine("SPACES: " + spaces); } }SPACES: 3
Here we consider 2 loops over a string
literal's character data. Each loop compares each character in the source string
against the space character.
char
directly. No allocations on the managed heap occur—this is a fast loop.for
-loop. It calls the ToString
method.ToString
to test the characters in a string
. Tested with .NET 5 on Linux.using System; using System.Diagnostics; class Program { static int A(string input) { int spaces = 0; for (int i = 0; i < input.Length; i++) { if (input[i] == ' ') { spaces++; } } return spaces; } static int B(string input) { int spaces = 0; for (int i = 0; i < input.Length; i++) { if (input[i].ToString() == " ") { spaces++; } } return spaces; } const int _max = 1000000; static void Main() { var s1 = Stopwatch.StartNew(); // Version 1: use direct char for-loop. for (int i = 0; i < _max; i++) { if (A("Dot Net Perls website") == 0) { return; } } s1.Stop(); var s2 = Stopwatch.StartNew(); // Version 2: use ToString in for-loop. for (int i = 0; i < _max; i++) { if (B("Dot Net Perls website") == 0) { return; } } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); } } 24.32 ns A 264.23 ns B (ToString)
Result
noteLet us consider the results. The for
-loop that uses direct character comparisons is far faster than the for
-loop that invokes the ToString
method.
The ToString
loop here is similar to code that loops over a string
by taking a one-character substring. This results in a string
allocation.
Substring
will be much slower.Chars should be directly tested in string
loops. Developers who are accustomed to other languages (like Java) sometimes make looping mistakes.