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

76

Ich hatte Probleme, die Unterschiede zwischen ILookup<TKey, TVal>und zu artikulieren IGrouping<TKey, TVal>, und bin gespannt, ob ich es jetzt richtig verstehe. LINQ verschärfte das Problem, indem es Sequenzen von IGroupingElementen produzierte und mir gleichzeitig eine ToLookupErweiterungsmethode gab. 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>>

Was sehr ähnlich aussieht:

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

Bin ich in den folgenden Analogien richtig?

  1. An IGrouping<TKey, TVal>ist eine einzelne Gruppe (dh eine Schlüsselfolge), analog dazu, KeyValuePair<TKey, TVal>wo der Wert tatsächlich eine Folge von Elementen ist (und nicht ein einzelnes Element).
  2. An IEnumerable<IGrouping<TKey, TVal>>ist eine Folge von diesen (ähnlich dem, was Sie erhalten, wenn Sie über ein iterierenIDictionary<TKey, TVal>
  3. An ILookup<TKey, TVal>ist eher wie a, IDictionary<TKey, TVal>wobei der Wert tatsächlich eine Folge von Elementen ist
mckamey
quelle

Antworten:

76

Ja, all das ist richtig.

Und ILookup<TKey, TValue>erstreckt sich auchIEnumerable<IGrouping<TKey, TValue>> so dass Sie auch alle der Schlüssel / Sammlung Paare iterieren kann als (oder statt) nur bestimmte Tasten nach oben.

Ich denke im Grunde ILookup<TKey,TValue>so IDictionary<TKey, IEnumerable<TValue>>.

Denken Sie daran, dass dies ToLookupeine "Jetzt tun" -Operation ist (sofortige Ausführung), während a zurückgestellt GroupBywird. Bei der Funktionsweise von "pull LINQ" müssen beim Abrufen von IGroupings aus dem Ergebnis von a GroupByohnehin alle Daten gelesen werden (da Sie die Gruppe nicht auf halbem Weg wechseln können), während dies bei anderen Implementierungen der Fall sein kann in der Lage sein, ein Streaming-Ergebnis zu erzeugen. (In Push LINQ; ich würde erwarten, dass LINQ to Events gleich ist.)

Jon Skeet
quelle
Danke für die Antwort. Ich bin noch nie auf Linq in Bezug auf Push / Pull gestoßen. Als ich zu Google ging, bin ich auf einen Ihrer Blogs gestoßen, also werde ich das überprüfen. Klingt nach einer interessanten Art, darüber nachzudenken.
McKamey
Ich denke immer noch, dass diese Unterschiede nicht zwei verschiedene Schnittstellen rechtfertigen. Ich denke, der Bibliotheksdesigner hätte sich für nur eine Schnittstelle entscheiden sollen. Vielleicht nur ein persönlicher Geschmack, der hier nicht explizit genug ist.
Rudimenter
Bin ich es oder gibt es Verwirrung mit GroupBy gegen GroupJoin? Diese sind nur konzeptionell verwandt. Vielleicht ist es nur ein Tippfehler in der Antwort.
sehe
@sehe: Ja, nur ein Tippfehler.
Jon Skeet
8

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

(Übrigens scheint es erwähnenswert zu sein, dass die Beziehung zwischen IEnumerable und IList der zwischen ILookup und IDictionary etwas ähnlich ist - die erstere ist unveränderlich, die letztere nicht.)

Carsten Führmann
quelle
4
Warum möchten Sie eine ILookup, die nur einen Einzelwert anstelle einer Gruppe hat? Das ist was ein Dictionaryist - oder ein ReadOnlyDictionaryoder IReadOnlyDictionarywenn du willst, dass es unveränderlich ist.
ErikE
5

GroupByund ToLookUphat fast die gleiche Funktionalität, AUSSER dies: Referenz

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

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

Lassen Sie uns den Unterschied anhand des Beispielcodes beseitigen. Angenommen, wir haben eine Klasse, die das PersonModell darstellt:

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

Danach definieren wir eine Liste von personnelswie 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 nach personnelsihrem Level gruppieren. Ich habe hier zwei Ansätze. mit GroupByoder ToLookUp. Wenn ich GroupBy, wie bereits erwähnt, die verzögerte Ausführung verwende, bedeutet dies, 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 die gruppiert personnels, aber bevor ich sie iterierte, habe ich einige entfernt personnels. Da GroupBydie verzögerte Ausführung verwendet wird, enthält das Endergebnis nicht die entfernten Elemente, da die Gruppierung in der berechnet wirdforeach Stelle berechnet wird.

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 GroupByist, außer dass er durch ersetzt wird. ToLookUp)

 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);
    }

Wenn ToLookUpdie ToLookUpMethode sofort ausgeführt wird, bedeutet dies, dass beim Aufrufen der Methode ein Ergebnis generiert und eine Gruppe angewendet wird. Wenn ich also ein Element aus entfernepersonnels vor der Iteration Auswirkungen auf das Endergebnis.

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: GroupByundToLookUp beide geben auch unterschiedliche Typen zurück.

Sie können ToDictionary anstelle von ToLookUp verwenden, müssen jedoch Folgendes beachten: ( Referenz )

Die Verwendung von ToLookup () ist der von ToDictionary () sehr ähnlich. Mit beiden können Sie Schlüsselauswahl, Wertauswahl und Vergleicher angeben. Der Hauptunterschied besteht darin, dass ToLookup () die doppelten Schlüssel zulässt (und erwartet), ToDictionary () jedoch nicht

Vahid Farahmandian
quelle
﹢1 für das Beispiel.
snr