ILookup <TKey, TVal> против IGrouping <TKey, TVal> - PullRequest
71 голосов
/ 27 августа 2009

У меня возникли проблемы с объяснением различий между 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 ]

71 голосов
/ 27 августа 2009

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

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

Я в основном думаю о ILookup<TKey,TValue> как о IDictionary<TKey, IEnumerable<TValue>>.

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

8 голосов
/ 05 июня 2013

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

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

0 голосов
/ 08 августа 2018

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 () не

...