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>

Правильно ли я в следующих аналогах?

  • An IGrouping<TKey, TVal> представляет собой одну группу (то есть последовательность с ключом), аналогичную KeyValuePair<TKey, TVal>, где значение фактически представляет собой последовательность элементов (а не один элемент)
  • An IEnumerable<IGrouping<TKey, TVal>> - это последовательность тех (аналогичных тем, которые вы получаете при итерации по IDictionary<TKey, TVal>
  • An ILookup<TKey, TVal> больше похож на IDictionary<TKey, TVal>, где значение фактически представляет собой последовательность элементов

Ответ 1

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

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

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

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

Ответ 2

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

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

Ответ 3

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, как указано ранее, он будет использовать отложенное выполнение, это означает, что при 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 ниже: (обратите внимание, что код такой же, как и предыдущий код, за исключением того, что 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 метод 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() не