Какой смысл в Lookup <TKey, TElement>?

155

MSDN объясняет поиск следующим образом:

А Lookup<TKey, TElement> напоминает Dictionary<TKey, TValue>. Разница в том, что Dictionary <TKey, TValue> отображает ключи на отдельные значения, тогда как Lookup <TKey, TElement> отображает ключи на коллекции значений.

Я не считаю это объяснение особенно полезным. Для чего используется Lookup?

дан-GPH
источник

Ответы:

215

Это нечто среднее между IGroupingи словарем. Он позволяет группировать элементы по ключу, а затем эффективно обращаться к ним через этот ключ (вместо того, чтобы просто перебирать их все, что GroupByпозволяет вам делать).

Например, вы можете загрузить множество типов .NET и создать поиск по пространству имен ... а затем очень легко получить доступ ко всем типам в определенном пространстве имен:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;

public class Test
{
    static void Main()
    {
        // Just types covering some different assemblies
        Type[] sampleTypes = new[] { typeof(List<>), typeof(string), 
                                     typeof(Enumerable), typeof(XmlReader) };

        // All the types in those assemblies
        IEnumerable<Type> allTypes = sampleTypes.Select(t => t.Assembly)
                                               .SelectMany(a => a.GetTypes());

        // Grouped by namespace, but indexable
        ILookup<string, Type> lookup = allTypes.ToLookup(t => t.Namespace);

        foreach (Type type in lookup["System"])
        {
            Console.WriteLine("{0}: {1}", 
                              type.FullName, type.Assembly.GetName().Name);
        }
    }
}

(Я бы обычно использовал varбольшинство этих объявлений в обычном коде.)

Джон Скит
источник
59
Я думаю, чтобы сделать этот ответ лучше, вы могли бы заменить некоторые из переменных. В учебных целях, я думаю, легче следовать, когда типы четко выражены. Просто мои 2 цента :)
Алекс Бараноски
3
Если в нем есть лучшее из обоих миров, то зачем использовать словарь?
Кайл Баран
15
@KyleBaran: Потому что это было бы бессмысленно для подлинных коллекций пар ключ / значение, где для каждого ключа существует только одно значение.
Джон Скит
12
@KyleBaran Lookup<,>- это просто неизменяемая коллекция ( Addнапример, без метода), которая имеет ограниченное использование. Кроме того, он не является коллекцией общего назначения в том смысле, что если вы выполняете поиск по несуществующему ключу, вы получаете пустую последовательность, а не исключение, которое имеет смысл только в особых контекстах, например, с linq. Это хорошо согласуется с тем фактом, что MS не предоставила открытый конструктор для класса.
nawfal
Порядок чтения ответов: jwg -> bobbymcr -> jonskeet
snr
58

Один из способов думать об этом: Lookup<TKey, TElement>это похоже на Dictionary<TKey, Collection<TElement>>. По сути, список из нуля или более элементов может быть возвращен через один и тот же ключ.

namespace LookupSample
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    class Program
    {
        static void Main(string[] args)
        {
            List<string> names = new List<string>();
            names.Add("Smith");
            names.Add("Stevenson");
            names.Add("Jones");

            ILookup<char, string> namesByInitial = names.ToLookup((n) => n[0]);

            // count the names
            Console.WriteLine("J's: {0}", namesByInitial['J'].Count()); // 1
            Console.WriteLine("S's: {0}", namesByInitial['S'].Count()); // 2
            Console.WriteLine("Z's: {0}", namesByInitial['Z'].Count()); // 0, does not throw
        }
    }
}
bobbymcr
источник
2
Могут ли быть нулевые элементы в результате поиска? Как бы вы это получили? (Насколько я могу судить, Lookup является публично неизменным, и я не думаю, что ToLookup будет эффективно изобретать ключи.)
Jon Skeet
8
Технически, да, поскольку Lookup возвращает пустую коллекцию для несуществующего ключа (я отредактировал свой пост, чтобы добавить пример кода, который показывает это).
bobbymcr
Порядок чтения ответов: jwg -> bobbymcr -> jonskeet
snr
Очень чистый и полезный ответ, я бы хотел, чтобы его выбрали.
минут
25

Одним из применений Lookupможет быть обратное Dictionary.

Предположим, у вас телефонная книга реализована в виде Dictionaryнабора (уникальных) имен в качестве ключей, каждое имя связано с номером телефона. Но два человека с разными именами могут использовать один и тот же номер телефона. Это не проблема для a Dictionary, которому все равно, что два ключа соответствуют одному значению.

Теперь вам нужен способ узнать, кому принадлежит данный номер телефона. Вы строите Lookup, добавляя все KeyValuePairsот вашего Dictionary, но в обратном направлении, со значением в качестве ключа и ключом в качестве значения. Теперь вы можете запросить номер телефона и получить список имен всех людей, чей номер телефона это. Сборка Dictionaryс теми же данными приведет к удалению данных (или к сбою, в зависимости от того, как вы это сделали), поскольку

dictionary["555-6593"] = "Dr. Emmett Brown";
dictionary["555-6593"] = "Marty McFly";

означает, что вторая запись перезаписывает первую - документа больше нет в списке.

Попытка записать одни и те же данные немного другим способом:

dictionary.Add("555-6593", "Dr. Emmett Brown");
dictionary.Add("555-6593", "Marty McFly");

выдает исключение во второй строке, поскольку вы не можете Addнайти ключ, который уже находится в Dictionary.

[Конечно, вы можете использовать какую-то другую единую структуру данных для поиска в обоих направлениях и т. Д. Этот пример означает, что вам нужно регенерировать Lookupиз Dictionaryкаждого изменения последнего. Но для некоторых данных это может быть правильным решением.]

ОРГ
источник
Ответ жизненно важен для понимания концепции. +1. Порядок чтения ответов: jwg -> bobbymcr -> jonskeet
snr
15

Я не успешно использовал его раньше, но вот мой путь:

A Lookup<TKey, TElement>будет вести себя почти как (реляционный) индекс базы данных для таблицы без уникального ограничения. Используйте его в тех же местах, что и другие.

Дарен Томас
источник
5

Полагаю, вы могли бы поспорить так: представьте, что вы создаете структуру данных для хранения содержимого телефонной книги. Вы хотите, чтобы ключ по lastName, а затем по firstName. Использование словаря здесь было бы опасно, потому что многие люди могут иметь одно и то же имя. Таким образом, словарь всегда будет максимально соответствовать одному значению.

Lookup отобразит потенциально несколько значений.

Lookup ["Смит"] ["Джон"] будет коллекция размером один миллиард.

Дэвид Андрес
источник
Ваш ответ вдохновил меня на дополнительный вопрос "Как ToLookup () с несколькими индексами?" , Как я могу воспроизвести такой, с несколькими индексами, поиск? Не могли бы вы ответить на него, используя любой другой образец или ссылку, где это возможно использовать Lookup["Smith"]["John"] ?
Полностью защищенный