Ich schaue in einer generischen Liste nach Elementen, die auf einem bestimmten Parameter basieren.
Was wäre im Allgemeinen die beste und schnellste Implementierung?
1. Durchlaufen Sie jedes Element in der Liste und speichern Sie jede Übereinstimmung in einer neuen Liste und geben Sie diese zurück
foreach(string s in list)
{
if(s == "match")
{
newList.Add(s);
}
}
return newList;
Oder
2. Verwenden der FindAll-Methode und Übergeben eines Delegaten.
newList = list.FindAll(delegate(string s){return s == "match";});
Laufen sie nicht beide in ~ O (N)? Was wäre die beste Praxis hier?
Viele Grüße, Jonathan
Sie sollten auf jeden Fall die FindAll
-Methode oder die entsprechende LINQ-Methode verwenden. Erwägen Sie auch, das knappere Lambda anstelle Ihres Delegierten zu verwenden, wenn Sie können (erfordert C # 3.0):
var list = new List<string>();
var newList = list.FindAll(s => s.Equals("match"));
Ich würde in diesem Fall die FindAll
-Methode verwenden, da sie prägnanter ist und IMO eine einfachere Lesbarkeit aufweist.
Sie haben Recht, dass sie so ziemlich beide in O(N) Zeit aufführen werden, obwohl die foreach
Anweisung sollte etwas schneller sein , vorausgesetzt, es muss kein Delegatenaufruf ausgeführt werden (Delegaten verursachen einen geringfügigen Mehraufwand im Gegensatz zu Methoden direkt aufrufen).
Ich muss betonen, wie unbedeutend dieser Unterschied ist, es wird höchstwahrscheinlich nie einen Unterschied bewirken, wenn Sie nicht eine massive Anzahl von Operationen an einer massive Liste.
Testen Sie wie immer, wo sich die Engpässe befinden, und handeln Sie entsprechend.
Jonathan,
Eine gute Antwort darauf finden Sie in Kapitel 5 (Überlegungen zur Leistung) von Linq To Action .
Sie messen a für jede Suche, die etwa 50-mal ausgeführt wird und die foreach = 68 ms pro Zyklus/List ergibt. Wirklich, es wäre wahrscheinlich in Ihrem Interesse, einfach einen Test zu erstellen und sich selbst davon zu überzeugen.
List.FindAll ist O(n) und durchsucht die gesamte Liste.
Wenn Sie einen eigenen Iterator mit foreach ausführen möchten, würde ich empfehlen, die Yield-Anweisung zu verwenden und wenn möglich ein IEnumerable zurückzugeben. Wenn Sie am Ende nur ein Element Ihrer Sammlung benötigen, ist dies schneller (da Sie Ihren Anrufer anhalten können, ohne die gesamte Sammlung zu erschöpfen).
Andernfalls bleiben Sie bei der BCL-Schnittstelle.
Jeder Perf-Unterschied wird extrem gering sein. Ich würde FindAll aus Gründen der Übersichtlichkeit vorschlagen oder, falls möglich, Enumerable.Where. Ich bevorzuge die Verwendung der Enumerable-Methoden, da sie eine größere Flexibilität bei der Umgestaltung des Codes ermöglichen (Sie brauchen keine Abhängigkeit von List<T>
).
Ja, beide Implementierungen sind O (n). Sie müssen sich jedes Element in der Liste ansehen, um alle Übereinstimmungen zu finden. Im Hinblick auf die Lesbarkeit würde ich auch FindAll bevorzugen. Zu Leistungsaspekten siehe LINQ in Aktion (Kap 5.3). Wenn Sie C # 3.0 verwenden, können Sie auch einen Lambda-Ausdruck anwenden. Aber das ist nur das Sahnehäubchen:
var newList = aList.FindAll(s => s == "match");
Ich bin mit den Lambdas
List<String> newList = list.FindAll(s => s.Equals("match"));
Sofern das C # -Team die Leistung für LINQ
und FindAll
nicht verbessert hat, scheint der folgende Artikel zu vermuten, dass for
und foreach
LINQ
und FindAll
bei der Objektaufzählung übertreffen würden: LINQ on Objects Performance .
Dieser Artikel wurde auf März 2009 datiert, kurz bevor dieser Beitrag ursprünglich gestellt wurde.