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