webentwicklung-frage-antwort-db.com.de

ILookup <TKey, TVal> vs. IGrouping <TKey, TVal>

Ich habe Probleme, die Unterschiede zwischen ILookup<TKey, TVal> und IGrouping<TKey, TVal> zu artikulieren, und bin gespannt, ob ich das jetzt richtig verstehe. LINQ hat das Problem noch verschärft, indem Sequenzen von IGrouping Elementen erstellt und mir gleichzeitig eine ToLookup Erweiterungsmethode gegeben wurden. Es fühlte sich so an, als wären sie gleich, bis ich genauer hinschaute.

var q1 = 
    from n in N
    group n by n.MyKey into g
    select g;
// q1 is IEnumerable<IGrouping<TKey, TVal>>

Welches ist gleichbedeutend mit:

var q2 = N.GroupBy(n => n.MyKey, n => n);
// q2 is IEnumerable<IGrouping<TKey, TVal>>

Welches sieht viel aus wie:

var q3 = N.ToLookup(n => n.MyKey, n => n);
// q3 is ILookup<TKey, TVal>

Bin ich in den folgenden Analogien richtig?

  1. Ein IGrouping<TKey, TVal> ist eine einzelne Gruppe (d. H. Eine Tastenfolge), analog zu KeyValuePair<TKey, TVal>, wobei der Wert tatsächlich eine Folge von Elementen ist (und nicht ein einzelnes Element).
  2. Ein IEnumerable<IGrouping<TKey, TVal>> ist eine Folge von diesen (ähnlich dem, was Sie erhalten, wenn Sie über einen IDictionary<TKey, TVal> iterieren).
  3. Ein ILookup<TKey, TVal> ähnelt eher einem IDictionary<TKey, TVal>, bei dem der Wert eine Folge von Elementen ist
70
mckamey

Ja, all das ist richtig.

Und ILookup<TKey, TValue> erweitert auch IEnumerable<IGrouping<TKey, TValue>>, sodass Sie alle Schlüssel-/Sammlungspaare durchlaufen sowie (oder stattdessen) nur bestimmte Schlüssel nachschlagen können.

Grundsätzlich stelle ich mir ILookup<TKey,TValue> als IDictionary<TKey, IEnumerable<TValue>> vor.

Bedenken Sie, dass ToLookup eine "Jetzt erledigen" -Operation ist (sofortige Ausführung), während GroupBy zurückgestellt wird. So wie "pull LINQ" funktioniert, muss IGroupings beim Abrufen von GroupBys ohnehin alle Daten lesen (da Sie die Gruppe nicht auf halbem Weg durchschalten können), während dies in anderen Implementierungen der Fall ist kann möglicherweise ein Streaming-Ergebnis erzeugen. (In Push LINQ ist dies der Fall. Ich würde erwarten, dass LINQ to Events identisch ist.)

70
Jon Skeet

Es gibt einen weiteren wichtigen Unterschied zwischen ILookup und IDictionary: Ersteres erzwingt Unveränderlichkeit in dem Sinne, dass es hier keine Methoden zum Ändern der Daten gibt (außer wenn der Consumer eine explizite Umwandlung vornimmt). Im Gegensatz dazu verfügt IDictionary über Methoden wie "Hinzufügen", mit denen die Daten geändert werden können. Aus der Sicht der funktionalen Programmierung und/oder der parallelen Programmierung ist ILookup also besser. (Ich wünschte, es gäbe auch eine Version von ILookup, die einem Schlüssel anstatt einer Gruppe nur einen Wert zuweist.)

(Übrigens sollte darauf hingewiesen werden, dass die Beziehung zwischen IEnumerable und IList der Beziehung zwischen ILookup und IDictionary etwas ähnlich ist - erstere ist unveränderlich, letztere nicht.)

8

GroupBy und ToLookUp haben fast die gleiche FunktionalitätAU&SZLIG;ERthis: Reference

GroupBy: Der GroupBy-Operator gibt Elementgruppen basierend auf einem Schlüsselwert zurück. Jede Gruppe wird durch ein Gruppierungsobjekt dargestellt.

ToLookup: ToLookup ist dasselbe wie GroupBy; Der einzige Unterschied besteht darin, dass die Ausführung von GroupBy zurückgestellt wird, während die Ausführung von ToLookup sofort erfolgt.

Hiermit können Sie den Unterschied mithilfe von Beispielcode beheben. Angenommen, wir haben eine Klasse, die das Modell Person repräsentiert:

class Personnel
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public int Level { get; set; }
}

danach definieren wir eine Liste von personnels wie folgt:

 var personnels = new List<Personnel>
    {
        new Personnel { Id = 1, FullName = "P1", Level = 1 },
        new Personnel { Id = 2, FullName = "P2", Level = 2 },
        new Personnel { Id = 3, FullName = "P3", Level = 1 },
        new Personnel { Id = 4, FullName = "P4", Level = 1 },
        new Personnel { Id = 5, FullName = "P5", Level =2 },
        new Personnel { Id = 6, FullName = "P6", Level = 2 },
        new Personnel { Id = 7, FullName = "P7", Level = 2 }
    };

Jetzt muss ich die personnels nach ihrem Level gruppieren. Ich habe hier zwei Ansätze. mit GroupBy oder ToLookUp. Wenn ich wie oben angegeben GroupBy verwende, wird die Ausführung verzögert ausgeführt. Dies bedeutet, dass beim Durchlaufen der Sammlung das nächste Element möglicherweise berechnet wird oder nicht, bis es angefordert wird.

 var groups = personnels.GroupBy(p => p.Level);
    personnels.RemoveAll(p => p.Level == 1);
    foreach (var product in groups)
    {
        Console.WriteLine(product.Key);
        foreach (var item in product)
            Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
    }

Im obigen Code habe ich zuerst personnels gruppiert, aber bevor ich es iterierte, habe ich einige personnels entfernt. Da GroupBy die verzögerte Ausführung verwendet, werden die entfernten Elemente im Endergebnis nicht berücksichtigt, da die Gruppierung hier im Punkt foreach erfolgt.

Ausgabe:

2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2

Aber wenn ich den obigen Code wie folgt umschreibe: (Beachten Sie, dass der Code mit dem vorherigen Code identisch ist, außer dass GroupBy durch ToLookUp ersetzt wird.)

 var groups = personnels.ToLookup(p => p.Level);
    personnels.RemoveAll(p => p.Level == 1);
    foreach (var product in groups)
    {
        Console.WriteLine(product.Key);
        foreach (var item in product)
            Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
    }

Da ToLookUp die sofortige Ausführung verwendet, bedeutet dies, dass beim Aufrufen der ToLookUp-Methode ein Ergebnis generiert und eine Gruppe angewendet wird. Wenn ich also vor der Iteration ein Element aus personnels entferne, wirkt sich dies nicht auf das Endergebnis aus.

Ausgabe:

1
1 >>> P1 >>> 1
3 >>> P3 >>> 1
4 >>> P4 >>> 1
2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2

Hinweis: GroupBy und ToLookUp geben auch unterschiedliche Typen zurück.

Möglicherweise verwenden Sie ToDictionary anstelle von ToLookUp. Beachten Sie jedoch Folgendes: ( reference )

Die Verwendung von ToLookup () ist der von ToDictionary () sehr ähnlich. In beiden Fällen können Sie Schlüssel-, Wert- und Vergleichselektoren angeben. Der Hauptunterschied besteht darin, dass ToLookup () die doppelten Schlüssel zulässt (und erwartet), ToDictionary () jedoch nicht

0