Die Anzahl eines Zeichens in einem String ermitteln
Heute wurde ich mal wieder mit einem kleinen Problemchen konfrontiert. Und zwar musst ich die Anzahl eines bestimmten Zeichens innerhalb eines Strings ermitteln. Kein Problem Dachte ich mir und fing an.
Während ich die Funktion schrieb, kam mir ein Gedanke. "Wie sieht es denn mit der Performance aus?"
Hmm. Eine gute Frage...
Rasch eine Neue Projektmappe aufgemacht und angefangen das zu Testen.
Meine erste Ansatz war jedes Zeichen des Strings durchzugehen und bei einer Übereinstimmung mit dem gesuchten Zeichen eine Zählervariable zu inkrementieren.
public static int FindCharWithForEach(string source, char searchFor)
{
var counter = 0;
foreach (char c in source)
{
if (c == searchFor) counter++;
}
return counter;
}
Als Testszenario hab ich mir einen Verzeichnispfad (C:\Windows\System32\drivers\etc) geschnappt und wollte wissen wie viele '\' darin enthalten sind.
Die Funktion lief ohne Probleme und gab mir nach 00:00:00.0003581 das korrekte Ergebnis von 4 zurück.
Mein zweiter Ansatz war, das ganze Mit LINQ umzusetzen.
public static int FindCharWithLinq(string source, char searchFor)
{
return source.Count(f => f == searchFor);
}
Auch diese Variante lief ohne Probleme. Nur mit einer Zeit von 00:00:00.0046630 hätte ich nicht gerechnet.
Ok. Dann suchen wir mal weiter.
Mein nächste Gedanke war den String einfach zu Splitten und die Anzahl der Elemente im Array (-1) zu zählen.
public static int FindCharWithSplit(string source, char searchFor)
{
return source.Split(searchFor).Length - 1;
}
Auch diese Lösung funktionierte. Allerdings war ich ein wenig überrascht, das diese Variante mit 00:00:00.0004546 doch deutlich schnellsten als die LINQ Version war.
Nachdem ich diese Resultate gesehen hatte, dachte ich mir, das ich den Test etwas Anspruchsvoller zu gestallten und duplizierte den Pfad ein paar mal um einen möglichst langen String zu erzeugen.
string value = "";
for(int i = 0 ; i < 10; i++)
value = value + @"C:\Windows\System32\drivers\etc";
Beim Test war das erwartete Ergebnis 40, da ich den Text einfach 10 mal hintereinander kopiert habe.
LINQ: | 40 hits in 00:00:00.0055382 |
Split: | 40 hits in 00:00:00.0004590 |
Count: | 40 hits in 00:00:00.0007574 |
Nach dem Ergebnis dacht ich mir eine kleine Serie draus zu bauen und habe noch Tests für Text x 100, x 1000, x 10000, x 100000 und x 1000000 laufen lassen.
Um LINQ noch kleine Zusatz Chance zu geben, habe ich noch PLINQ mit dazu genommen. Der PC auf dem die Tests laufen ist ein einfacher Dual Core mit einem Intel Core2Duo P9700 mit 2x2.80GHz.
| 10 | 100 | 1.000 |
---|---|---|---|
LINQ | 40 hits in 00:00:00.0055382 | 400 hits in 00:00:00.0089991 | 4000 hits in 00:00:00.0724733 |
Split | 40 hits in 00:00:00.0004590 | 400 hits in 00:00:00.0004476 | 4000 hits in 00:00:00.0008498 |
Count | 40 hits in 00:00:00.0007574 | 400 hits in 00:00:00.0024534 | 4000 hits in 00:00:00.0204854 |
PLINQ | 40 hits in 00:00:00.6701543 | 400 hits in 00:00:00.2043689 | 4000 hits in 00:00:00.2359259 |
10.000 | 100.000 | 1.000.000 | |
---|---|---|---|
LINQ | 40000 hits in 00:00:00.5656169 | 400000 hits in 00:00:05.5925695 | 4000000 hits in 00:00:56.1154690 |
Split | 40000 hits in 00:00:00.0045024 | 400000 hits in 00:00:00.0489870 | 4000000 hits in 00:00:00.9762967 |
Count | 40000 hits in 00:00:01.8509596 | 400000 hits in 00:00:03.4958518 | 4000000 hits in 00:00:38.0938921 |
PLINQ | 40000 hits in 00:00:00.6517095 | 400000 hits in 00:00:04.8506172 | 4000000 hits in 00:00:45.4006163 |
Ich glaub zu den Ergebnissen bracht man nicht mehr viel sagen, da Diese sehr eindeutig sind.
LINQ ist kein Allheilmittel und die einfachsten Wege sind nicht unbedingt die Besten.
Ich habe nun meinen Favoriten. :)
For so long,
CJ