ILookup <TKey, TVal> проти IGrouping <TKey, TVal>


76

У мене були проблеми з формулюванням відмінностей між ILookup<TKey, TVal>і IGrouping<TKey, TVal>, і мені цікаво, якщо я зараз це правильно розумію. LINQ ускладнив проблему, створивши послідовності IGroupingелементів, одночасно надавши мені ToLookupметод розширення. Тож відчувалося, що вони однакові, поки я не придивився уважніше.

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

Що еквівалентно:

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

Що дуже схоже на:

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

Чи я прав у наступних аналогіях?

  1. An IGrouping<TKey, TVal>- це одна група (тобто послідовність, що вводиться в ключ), аналогічна тому, KeyValuePair<TKey, TVal>де значення насправді є послідовністю елементів (а не одним елементом)
  2. An IEnumerable<IGrouping<TKey, TVal>>- це послідовність цих (подібних до того, що ви отримуєте при ітерації надIDictionary<TKey, TVal>
  3. An ILookup<TKey, TVal>більше схожий на a, IDictionary<TKey, TVal>де значення насправді є послідовністю елементів

Відповіді:


76

Так, все це правильно.

А ILookup<TKey, TValue>також розширюється, IEnumerable<IGrouping<TKey, TValue>>щоб ви могли переглядати всі пари ключів / колекцій, а також (або замість) просто шукати певні ключі.

В основному я вважаю ILookup<TKey,TValue>, що подібний IDictionary<TKey, IEnumerable<TValue>>.

Майте на увазі, що ToLookupце операція "зроби зараз" (негайне виконання), тоді як a GroupByвідкладено. Як це трапляється, з тим, як працює "pull LINQ", коли ви починаєте витягувати IGroupings з результату a GroupBy, він все одно повинен прочитати всі дані (оскільки ви не можете переключити групу посередині), тоді як в інших реалізаціях це може мати можливість отримати потоковий результат. (Це відбувається в Push LINQ; я би очікував, що LINQ to Events буде однаковим.)


Дякую за відповідь. Я раніше не стикався з думкою про Linq з точки зору push / pull. Коли я зайшов у Google, то натрапив на один із ваших блогів, тож я перевірю це. Звучить цікавий спосіб думати про це.
mckamey

Я все ще думаю, що ці відмінності не виправдовують 2 різних інтерфейсів. Я думаю, що дизайнер Бібліотеки повинен був вирішити лише один інтерфейс. Можливо, просто особистий смак, що тут є чимось недостатньо явним.
зачаток

Це я, чи є якась плутанина з GroupBy проти GroupJoin? Це лише концептуально пов’язано. Можливо, це просто помилка у відповіді.
sehe

8

Існує ще одна важлива відмінність між ILookup та IDictionary: перший забезпечує незмінність у тому сенсі, що тут немає методів зміни даних (за винятком випадків, коли споживач виконує явний привід). На відміну від цього, IDictionary має такі методи, як "Додати", які дозволяють змінювати дані. Отже, з точки зору функціонального програмування та / або паралельного програмування, ILookup є приємнішим. (Мені б хотілося, щоб була також версія ILookup, яка присвоює ключу лише одне значення, а не групі.)

(До речі, варто зауважити, що зв'язок між IEnumerable та IList дещо схожий на зв'язок між ILookup та IDictionary - перший є незмінним, другий - ні.)


4
Чому ви хочете, щоб значення, ILookupяке має лише значення одного елемента, а не групи? Це те, що Dictionaryє - або a, ReadOnlyDictionaryабо IReadOnlyDictionaryякщо ви хочете, щоб воно було незмінним.
ErikE

5

GroupByі ToLookUpмає майже таку ж функціональність КРІМ це: Посилання

GroupBy: Оператор GroupBy повертає групи елементів на основі деякого значення ключа. Кожна група представлена ​​об'єктом IGrouping.

ToLookup: ToLookup те саме, що і GroupBy; єдина відмінність - виконання GroupBy відкладено, тоді як виконання ToLookup відбувається негайно.

Давайте очистимо різницю, використовуючи зразок коду припустимо, що у нас є клас, що представляє Personмодель:

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

після цього ми визначаємо список, personnelsяк показано нижче:

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

Тепер мені потрібно отримати personnelsзгруповані за їх рівнем. У мене тут два підходи. використовуючи GroupByабо ToLookUp. Якщо я використовую GroupBy, як зазначено раніше, він буде використовувати відкладене виконання, це означає, що коли ви переглядаєте колекцію, наступний елемент може обчислюватися, а може і не обчислюватися, доки його не буде викликано.

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

У наведеному вище коді я спочатку згрупував файл personnels, але перед його повторенням видалив деякі personnels. Оскільки GroupByвикористовується відкладене виконання, тому кінцевий результат не включатиме вилучені елементи, оскільки групування буде обчислюватися в foreachточці тут.

Вихід:

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

Але якщо я перепишу наведений вище код, як показано нижче: (зверніть увагу, що код такий самий, як і попередній код, за винятком того GroupBy, що замінюється на 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);
    }

Оскільки ToLookUpвикористовується негайне виконання, це означає, що коли я викликаю ToLookUpметод, генерується результат і застосовується група, тому, якщо я вилучу будь-який елемент personnelsдо ітерації, це не вплине на кінцевий результат.

Вихід:

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

Примітка: GroupByі ToLookUpобидва також повертають різні типи.

Ви можете використовувати ToDictionary замість ToLookUp, але вам потрібно звернути увагу на це :( посилання )

Використання ToLookup () дуже схоже на використання ToDictionary (), обидва дозволяють вказати селектори ключів, селектори значень та порівняльники. Основна відмінність полягає в тому, що ToLookup () дозволяє (і очікує) дублікатів ключів, тоді як ToDictionary () не


﹢1 для прикладу.
snr
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.