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.
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.
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);
}
}SPACES: 3
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.
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).