Por que ToLookup e GroupBy são diferentes?

111

.ToLookup<TSource, TKey>retorna um ILookup<TKey, TSource>. ILookup<TKey, TSource>também implementa interface IEnumerable<IGrouping<TKey, TSource>>.

.GroupBy<TSource, TKey>retorna um IEnumerable<IGrouping<Tkey, TSource>>.

ILookup tem a propriedade indexer útil, portanto, pode ser usado de maneira semelhante a um dicionário (ou semelhante a uma consulta), enquanto o GroupBy não pode. O GroupBy sem o indexador é difícil de trabalhar; praticamente a única maneira de fazer referência ao objeto de retorno é fazendo um loop por ele (ou usando outro método de extensão LINQ). Em outras palavras, em qualquer caso em que GroupBy funcione, ToLookup funcionará também.

Tudo isso me deixa com a pergunta: por que eu deveria me preocupar com o GroupBy? Por que deveria existir?

Shlomo
fonte
7
GroupByÉ IQuerable, ILookupnão é
Magnus
5
GroupBy não enumera a lista ToLookup enumera da mesma maneira ToList / ToArray
Aducci
3
Eu indiquei isso para reabertura, pois a pergunta que é supostamente uma duplicata é sobre IGrouping em vez de GroupBy e ILookup em vez de ToLookup . As diferenças entre eles são diferentes das diferenças entre eles. Isso deve ser aparente a partir das diferenças nas respostas entre as perguntas.
Sam
1
ambos criam um Lookup, mas o GroupBycria quando o resultado é enumerado referenceource.microsoft.com/#System.Core/System/Linq/…
Slai

Respostas:

175

por que eu iria me preocupar com GroupBy? Por que deveria existir?

O que acontece quando você chama ToLookup em um objeto que representa uma tabela de banco de dados remoto com um bilhão de linhas?

O bilhão de linhas é enviado pela rede e você constrói a tabela de pesquisa localmente.

O que acontece quando você chama GroupBy em um objeto assim?

Um objeto de consulta é construído; fim da história.

Quando esse objeto de consulta é enumerado, a análise da tabela é feita no servidor de banco de dados e os resultados agrupados são enviados de volta sob demanda, alguns de cada vez.

Logicamente, eles são a mesma coisa, mas as implicações de desempenho de cada um são completamente diferentes. Chamar ToLookup significa que quero um cache de tudo agora organizado por grupo . Chamar GroupBy significa "Estou construindo um objeto para representar a pergunta 'como seriam essas coisas se eu as organizasse em grupos?'"

Eric Lippert
fonte
6
O pôster não visa especificamente uma IQueryable<T>representação. Sua resposta cobre essa situação, mas quando é simples IEnumerable<T>(LINQ-to-Objects) pode parecer que não há motivo para usar um em vez do outro, que é o que acredito que @Shlomo está tentando chegar. Não é o IQueryable<T>caso, mas o caso LINQ-to-Objects.
casperOne
21
@casperOne: Acho que você não entendeu meu ponto. Mesmo no caso do LINQ para objetos, chamar GroupBy ainda não itera na coleção. (Como Aducci apontou na resposta que você excluiu.) Essa é uma diferença fundamental.
Eric Lippert de
12
@EricLippert: Mas isso é apenas um efeito colateral da implementação ou é garantido que o enumerável será iterado quando você chamar ToLookup, não importa quais alterações sejam feitas na implementação?
9
@Will: Você fez uma observação excelente; a documentação não garante que ToLookup esteja "ansioso". Provavelmente deve notar isso.
Eric Lippert de
10
A ansiedade explica isso. A linguagem de 'ToMetaType' eu acho que implica ansiedade; embora seja obviamente deixado para a implementação. Os outros 'To's estão todos ansiosos (ToList, ToArray, ToDictionary). Obrigado rapazes.
Shlomo
98

Em palavras simples do mundo LINQ:

  • ToLookup() - execução imediata
  • GroupBy() - execução adiada
sll
fonte
17

Os dois são semelhantes, mas são usados ​​em cenários diferentes. .ToLookup()retorna um objeto pronto para usar que já possui todos os grupos (mas não o conteúdo do grupo) carregados avidamente. Por outro lado, .GroupBy()retorna uma sequência de grupos carregada lentamente.

Diferentes provedores de LINQ podem ter comportamentos diferentes para o carregamento rápido e lento dos grupos. Com LINQ-to-Object provavelmente faz pouca diferença, mas com LINQ-to-SQL (ou LINQ-to-EF, etc.), a operação de agrupamento é realizada no servidor de banco de dados em vez do cliente e, portanto, você pode desejar para fazer uma filtragem adicional na chave do grupo (o que gera uma HAVINGcláusula) e então obter apenas alguns dos grupos em vez de todos eles. .ToLookup()não permitiria tal semântica, uma vez que todos os itens são agrupados avidamente.

Allon Guralnek
fonte