LINQ in SortedList

8

Ich bin ein kompletter LINQ-Neuling, also weiß ich nicht, ob mein LINQ für das, was ich tun muss, falsch ist oder ob meine Erwartungen an die Leistung zu hoch sind.

Ich habe eine SortedList von Objekten, die von int codiert sind; SortedList im Gegensatz zu SortedDictionary, weil ich die Sammlung mit vorsortierten Daten füllen werde. Meine Aufgabe ist es, entweder den genauen Schlüssel oder, wenn es keinen genauen Schlüssel gibt, den nächsthöheren Wert zu finden. Wenn die Suche für die Liste zu hoch ist (z. B. der höchste Schlüssel ist 100, aber suchen Sie nach 105), geben Sie null zurück.

%Vor%

Bei einer Liste von 50.000 Datensätzen dauert der Aufruf von getItem 500 mal ungefähr eineinhalb Sekunden. Ein Anruf von 50.000 Mal dauert 2 Minuten. Diese Leistung scheint sehr schlecht zu sein. Ist mein LINQ schlecht? Erwarte ich zu viel? Soll ich meine eigene binäre Suchfunktion starten?

    
Chris Simmons 24.05.2010, 14:36
quelle

6 Antworten

5

Das Schreiben einer binären Suche kann schwierig sein.

Zum Glück hat Microsoft bereits eine ziemlich robuste geschrieben: Array.BinarySearch<T> . Dies ist in der Tat , die Methode, die SortedList<TKey, TValue>.IndexOfKey intern verwendet . Das einzige Problem ist, dass es ein T[] Argument anstelle von IList<T> (wie SortedList<TKey, TValue>.Keys ) benötigt.

Weißt du was? Es gibt dieses großartige Tool namens Reflektor , mit dem Sie sich den .NET-Quellcode ansehen können ...

Probieren Sie es aus: eine generische BinarySearch Erweiterungsmethode für IList<T> , die direkt aus dem reflektierten Code der Microsoft Array.BinarySearch<T> Implementierung stammt.

%Vor%

Damit können Sie list.Keys.BinarySearch aufrufen und das negative bitweise Komplement des gewünschten Indexes erhalten, falls der gewünschte Schlüssel nicht gefunden wird (das Folgende wird im Wesentlichen direkt aus tzaman's Antwort genommen):

%Vor%     
Dan Tao 24.05.2010, 19:27
quelle
7

Zuerst wird Ihre Anfrage zweimal ausgewertet (einmal für Any und einmal für Min ). Zweitens erfordert Min , dass es über die gesamte Liste iteriert, obwohl die Tatsache, dass es sortiert ist, bedeutet, dass das erste Element das Minimum ist. Sie sollten dies ändern können:

%Vor%

Dazu:

%Vor%

AKTUALISIEREN

Da Sie einen Werttyp auswählen, gibt FirstOrDefault kein Nullable-Objekt zurück. Ich habe Ihre Abfrage so geändert, dass der ausgewählte Wert stattdessen in int? umgewandelt wird, sodass der resultierende Wert auf null überprüft werden kann. Ich würde dies für die Verwendung von ContainsKey empfehlen, da dies true zurückgeben würde, wenn Ihre Liste einen Wert für 0 enthalten würde. Angenommen, Sie haben die folgenden Werte

0 2 4 6 8

Wenn Sie etwas kleiner oder gleich 8 eingeben, erhalten Sie den richtigen Wert. Wenn Sie jedoch 9 übergeben, erhalten Sie 0 ( default(int) ), das ist in der Liste, aber ist nicht ein gültiges Ergebnis.

    
Adam Robinson 24.05.2010 14:41
quelle
3

Wenn Sie LINQ für SortedList verwenden, profitieren Sie nicht von der Sortierung.

Für optimale Leistung sollten Sie Ihre eigene binäre Suche schreiben.

    
SLaks 24.05.2010 14:39
quelle
2

OK, nur um ein bisschen mehr Sichtbarkeit zu geben - hier ist eine prägnantere Version von Adam Robinsons Antwort:

%Vor%

Die Funktion FirstOrDefault hat eine Überladung, die ein Prädikat akzeptiert, das das erste Element auswählt, das eine Bedingung erfüllt - Sie können das Element direkt verwenden, um das gewünschte Element zu erhalten, oder null , wenn es nicht existiert.

    
tzaman 25.05.2010 04:47
quelle
1

Warum nicht das BinarySearch verwenden, das in der Klasse List eingebaut ist?

%Vor%

Wenn das Suchziel nicht in der Liste ist, gibt BinarySearch das bitweise Komplement des nächsthöheren Elements zurück; wir können das verwenden, um Sie direkt zu bekommen, was Sie wollen, indem Sie das Ergebnis erneut ergänzen, wenn es negativ ist. Wenn es gleich Count wird, war Ihr Suchschlüssel größer als alles in der Liste.

Dies sollte viel schneller sein als ein LINQ where , da es bereits sortiert ist ... Wie die Kommentare gezeigt haben, erzwingt der Aufruf ToList eine Auswertung der gesamten Liste. Dies ist nur von Vorteil, wenn Sie mehrere Suchvorgänge durchführen, ohne die zugrundeliegende SortedList zu ändern, und die keys -Liste getrennt herum behalten.

    
tzaman 24.05.2010 15:32
quelle
0

Wenn Sie OrderedDictionary in PowerCollections verwenden, können Sie einen Enumerator erhalten, der dort beginnt, wo der gesuchte Schlüssel sein sollte ... wenn er nicht vorhanden ist, erhalten Sie den nächsten Knoten und können dann in O vor / zurück navigieren (log N) Zeit pro nav Anruf.

Dies hat den Vorteil, dass Sie nicht Ihre eigene Suche schreiben müssen oder sogar Ihre eigenen Suchen über einer SortedList verwalten müssen.

    
huntharo 11.02.2013 19:15
quelle