Lookup<TKey, TElement> 的意义何在?

Posted

技术标签:

【中文标题】Lookup<TKey, TElement> 的意义何在?【英文标题】:What is the point of Lookup<TKey, TElement>? 【发布时间】:2010-11-27 01:43:42 【问题描述】:

MSDN 是这样解释 Lookup 的:

Lookup&lt;TKey, TElement&gt; 类似于Dictionary<TKey, TValue>。不同的是,一个 Dictionary 将键映射到单个值,而 Lookup 将键映射到值的集合。

我不觉得这个解释特别有用。 Lookup 有什么用?

【问题讨论】:

【参考方案1】:

它是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 用于大多数这些声明。)

【讨论】:

我认为为了使这个答案更好,您可以替换一些变量。出于学习目的,我认为当类型表达清楚时更容易理解。只是我的 2 美分 :) 如果它两全其美,那为什么还要麻烦字典呢? @KyleBaran:因为对于真正的键/值对集合来说毫无意义,因为每个键只有一个值。 @KyleBaran Lookup&lt;,&gt; 只是一个使用有限的不可变集合(例如没有Add 方法)。此外,它不是一个通用的集合,因为如果您在不存在的键上进行查找,您会得到一个空序列而不是异常,这仅在特殊上下文中有意义,例如使用 linq。这与 MS 没有为该类提供公共构造函数这一事实相得益彰。 阅读答案顺序是 jwg -> bobbymcr -> jonskeet【参考方案2】:

我之前没有成功使用过,但这是我的尝试:

Lookup&lt;TKey, TElement&gt; 的行为与没有唯一约束的表上的(关系)数据库索引非常相似。在你会使用另一个的地方使用它。

【讨论】:

【参考方案3】:

一种思考方式是:Lookup&lt;TKey, TElement&gt; 类似于 Dictionary&lt;TKey, Collection&lt;TElement&gt;&gt;。基本上可以通过相同的键返回零个或多个元素的列表。

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
        
    

【讨论】:

查找结果中可以有零个元素吗?你怎么得到那个? (据我所知,Lookup 是公开不可变的,我认为 ToLookup 不会有效地发明密钥。) 从技术上讲,是的,因为查找返回一个不存在的键的空集合(我编辑了我的帖子以添加一个显示此的代码示例)。 阅读答案顺序是 jwg -> bobbymcr -> jonskeet 一个非常干净和有用的答案,我希望它被选中。【参考方案4】:

我想你可以这样说:想象你正在创建一个数据结构来保存电话簿的内容。您想按姓氏然后按名字键入。在这里使用字典会很危险,因为很多人可以有相同的名字。因此,字典将始终最多映射到单个值。

查找将映射到可能的多个值。

Lookup["Smith"]["John"] 将是一个大小为 10 亿的集合。

【讨论】:

您的回答启发了我的后续问题"How ToLookup() with multiple indexes?"。我怎样才能重现这样的,具有多个索引,查找?您能否使用可以使用Lookup["Smith"]["John"] 的任何其他示例或参考来回答它?【参考方案5】:

Lookup 的一个用途可能是反转 Dictionary

假设您有一个以Dictionary 实现的电话簿,其中有一堆(唯一)名称作为键,每个名称都与一个电话号码相关联。但是两个不同名字的人可能共用同一个电话号码。这对于Dictionary 来说不是问题,它并不关心两个键对应相同的值。

现在您需要一种方法来查找给定电话号码属于谁。您构建一个Lookup,从Dictionary 中添加所有KeyValuePairs,但向后,以值作为键,键作为值。您现在可以查询电话号码,并获取电话号码所在的所有人的姓名列表。使用相同的数据构建Dictionary 会丢弃数据(或失败,具体取决于您的操作方式),因为这样做

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

表示第二个条目会覆盖第一个条目 - Doc 不再列出。

尝试以稍微不同的方式写入相同的数据:

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

将在第二行引发异常,因为您不能 Add 已在 Dictionary 中的密钥。

[当然,您可能希望使用其他一些单一数据结构来进行双向查找等。此示例意味着您必须在每次 Dictionary 更改时从 Lookup 重新生成 Lookup。但对于某些数据,它可能是正确的解决方案。]

【讨论】:

答案对于理解这个概念至关重要。 +1。阅读答案顺序是 jwg -> bobbymcr -> jonskeet

以上是关于Lookup<TKey, TElement> 的意义何在?的主要内容,如果未能解决你的问题,请参考以下文章

Linq语法及用法

将 Dictionary<TKey, List<TValue>> 转换为 ReadOnlyDictionary<TKey, ReadOnlyCollection<T

读书笔记 C# Linq查询之group关键字浅析

两个字典<TKey, TValue> 到字典<TKey, List<TValue>> 的并集

自己实现一个简单的ArrayList

.NET 4 中的 IDictionary<TKey, TValue> 不是协变的