Ich muss zwei sehr große Sammlungen mit möglicherweise fehlenden Elementen vergleichen

8

Ich bin in einem Programm, das ich für meine Arbeit schreibe, auf eine Mauer gestoßen. Sie müssen den Kontext nicht speziell kennen, aber kurz gesagt, ich habe zwei Sammlungen von ungefähr ~ 650k Datensätzen.

Nehmen wir an, dass die Sammlung A die richtige ist, und die Sammlung B ist diejenige, von der ich weiß, dass sie falsch ist.

Sammlung B enthält ein komplexes Objekt, das eine Eigenschaft des gleichen Typs wie die Elemente in Sammlung A hat (mit anderen Worten, es sieht ungefähr so ​​aus):

%Vor%

Mein Problem ist, dass ich im Grunde über A aufzählen muss, um zu sehen, ob das aktuelle Element von A in einem komplexen Objekt in B existiert; Wenn es nicht existiert, muss ich ein komplexes Objekt erstellen, das dieses Element (unter anderem) kapselt.

Das Problem tritt auf, wenn ich feststelle, dass beide Listen 650.000 Elemente lang sind, ca. Ich kann den Datensatz nicht reduzieren; Ich muss diese 650.000 verwenden. Im Moment habe ich ICollection.Contains() verwendet und eine (naive) Implementierung der Binärsuche versucht, aber das dauert einfach viel zu lange.

Hast du Vorschläge für mich?

EDIT: Wenn es hilft, implementiert T IComparable. EDIT2: Etwas mehr Kontext: Der IEnumerable wird von einer DataTable mithilfe von Linq To Objects abgerufen.

%Vor%

Aus Gründen der Vollständigkeit, falls diese Frage archiviert wird und jemand anderes dieses Problem beheben muss, sah meine aktuelle Implementierung ein bisschen so aus.

%Vor%

Dank Cosmin ist dieses Problem jetzt gelöst und die fertige Implementierung ist dies:             Liste erwartet = neue Liste ();             DateTime current = RoundSeconds (neue DateTime (start.Time.Ticks));

%Vor%     
Dan Pantry 11.10.2013, 07:52
quelle

5 Antworten

4

Schritt für Schritt:

  • Verwenden Sie eine der allgemeinen Sammlungen O(1) , um eine schnell durchsuchbare Liste von T zu erstellen, die sich bereits in der zweiten Sammlung befinden. Darf ich vorschlagen HashSet<T>
  • Zählen Sie über die Sammlung SECOND auf und fügen Sie alle T aus dem ersten Schritt in die Sammlung ein.
  • Zählen Sie die ERSTE Sammlung auf und prüfen Sie, ob sich jedes Element in der im ersten Schritt erstellten Sammlung befindet. Da diese Operation O(1) ist, hast du jetzt eine O(n) Lösung.
  • Genießen.

Hier ist eine Klasse, die diesen Algorithmus als generische Erweiterungsmethode implementiert, um sie LINQ-freundlicher zu machen. Gemacht nimm man seine Argumente als IEnumerable<T> und return IEnumerable<T> , machte keine Annahmen über die Typen ( T und Complex ). In meinem Test verwende ich eine Liste von Tuple<int,int> als einen komplexen Typ und eine einfache int als den einfachen Typ. Die Konsolenanwendung füllt List<Tuple<int,int>> mit 600000 Werten und setzt 100000 Werte in die einfache List<int> , die einen Enumerator verwendet, um alle einfachen Werte zu zählen, die nicht in List<Tuple<int,int>> gefunden wurden. Es ist so schnell, dass du keine Chance hast es zu sehen, wenn es funktioniert. Wenn du F5 drückst, zeigt es nur das Ergebnis an.

%Vor%     
Cosmin Prund 11.10.2013, 07:56
quelle
0

Ich würde vorschlagen, Ihre komplexen Objekte in einem Dictionary zu speichern, indem Sie die A-Type-Eigenschaft als Schlüssel verwenden. Dann können Sie sehen, ob ein Element in A sehr schnell im Dictionary existiert. Jede Suchoperation wird O (1) sein, und die Gesamtleistung sollte O (n) sein.

Alternativ können Sie .ToLookup () für Ihr vorhandenes IEnumerable aufrufen und den Schlüssel und Wert in einem Lambda-Ausdruck erstellen. Der zurückgegebene ILookup sollte perfekt für Ihre Bedürfnisse sein (d. H. Nach Werten von A suchen). Der zusätzliche Vorteil hier ist, dass wenn Sie mehrere komplexe Objekte mit A haben, erhalten Sie eine Sammlung von ihnen zurück, wenn Sie die Suche verwenden.

    
Baldrick 11.10.2013 08:02
quelle
0

Wenn ich Ihre Anforderung verstanden habe, dann funktioniert dieser Code gut. Ich habe es auf 6.5m Datensätze für beide Sammlungen erhöht und es in 11 Sekunden abgeschlossen. Die Reduzierung auf 650k Datensätze dauert weniger als eine Sekunde.

%Vor%     
Enigmativity 11.10.2013 08:33
quelle
0

UPDATE: Hören Sie zuallererst auf, die Datasets zu verwenden. Ich schlage vor, Sie verwenden Linq zu SQL oder Entity Framework.

Versuchen Sie Folgendes:

%Vor%

Das sollte schneller sein, aber messen.

Dann probiere

aus %Vor%

Und nochmals messen.

Stellen Sie sicher, dass der Typ der Objekte in A GetHashCode() und Equals(object) correcty und effizient überschreibt. Insbesondere GetHashCode() sollte eine hohe Wahrscheinlichkeit haben, dass verschiedene Objekte einen anderen Hash-Code haben und trotzdem schnell sind.

UPDATE: Da wir jetzt wissen, dass der Typ der Objekte in A DateTime ist, ist die Anforderung für GetHashCode() und Equals(object) OK.

Der Code wird

%Vor%     
Kris Vandermotten 11.10.2013 08:10
quelle
0

Wenn Sie Listen mit demselben Schlüssel sortieren, können Sie beide gleichzeitig durchlaufen. Auf diese Weise konnte ich die Zeit auf weniger als die von contains oder except und sogar noch weniger als die toLookup-Zeiten, die ich sah, heruntersetzen.

%Vor%

Um die Testdaten zu erzeugen, habe ich folgendes gemacht:

%Vor%     
Gary.S 11.10.2013 08:59
quelle

Tags und Links