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

У меня были проблемы с формулированием различий между 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. IGrouping<TKey, TVal> является одной группой (то есть последовательность ключей), аналогично KeyValuePair<TKey, TVal> где значение на самом деле представляет собой последовательность элементов (а не один элемент)
  2. IEnumerable<IGrouping<TKey, TVal>> представляет собой последовательность из них (аналогично тому, что вы получаете при итерации по IDictionary<TKey, TVal>
  3. ILookup<TKey, TVal> больше похоже на IDictionary<TKey, TVal> где значение на самом деле является последовательностью элементов

3 ответа

Решение

Да, все это правильно.

А также ILookup<TKey, TValue> также расширяется IEnumerable<IGrouping<TKey, TValue>> так что вы можете перебирать все пары ключ / коллекция, а также (или вместо) просто искать конкретные ключи.

Я в основном думаю о ILookup<TKey,TValue> как быть IDictionary<TKey, IEnumerable<TValue>>,

Имейте в виду, что ToLookup является операцией "сделать это сейчас" (немедленное выполнение), тогда как GroupBy отложено Как это происходит, с тем, как работает "тянуть LINQ", когда вы начинаете тянуть IGroupingс в результате GroupByв любом случае, он должен прочитать все данные (потому что вы не можете переключить группу на полпути), тогда как в других реализациях он может дать результат потоковой передачи. (Это происходит в Push LINQ; я ожидаю, что LINQ to Events будет таким же.)

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 () не делает

Есть еще одно важное различие между ILookup и IDictionary: первое обеспечивает неизменность в том смысле, что здесь нет методов для изменения данных (кроме случаев, когда потребитель выполняет явное приведение). В отличие от этого, в IDictionary есть такие методы, как "Добавить", которые позволяют изменять данные. Итак, с точки зрения функционального программирования и / или параллельного программирования, ILookup лучше. (Я только хотел бы, чтобы была также версия ILookup, которая назначает только одно значение ключу, а не группе.)

(Между прочим, стоит отметить, что отношения между IEnumerable и IList чем-то похожи на отношения между ILookup и IDictionary - первое является неизменным, а второе - нет.)

Другие вопросы по тегам