for String Loop, Count Spaces
This page was last reviewed on Sep 12, 2023.
Dot Net Perls
String for-loop. The "for" keyword can loop over characters in strings. But some code examples use an inefficient pattern. This causes unnecessary allocations on the managed heap.
Shows a string loop
An optimization. By directly accessing characters, not strings, loops can be improved and simplified. Many programs can be improved.
Requirements. 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.Shows a string loop
Input = Dot Net Perls website Spaces = 3
First example. 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); } }
Benchmark. 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.
Here We count spaces in the string with 2 loops. The first version is much faster—it should always be used.
Version 1 The first loop checks each char directly. No allocations on the managed heap occur—this is a fast loop.
Version 2 The second loop in the program also uses a for-loop. It calls the ToString method.
Result It is much faster to avoid calling 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 note. Let us consider the results. The for-loop that uses direct character comparisons is far faster than the for-loop that invokes the ToString method.
Another note. 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.
Note If you are testing characters, you also do not need to do this. Loops that use Substring will be much slower.
String Substring
A summary. Chars should be directly tested in string loops. Developers who are accustomed to other languages (like Java) sometimes make looping mistakes.
Dot Net Perls is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.
This page was last updated on Sep 12, 2023 (edit).
© 2007-2024 Sam Allen.