【问题标题】:Difference between Lookup() and Dictionary(Of list())Lookup() 和 Dictionary(Of list()) 的区别
【发布时间】:2012-11-01 23:38:27
【问题描述】:

我正试图围绕哪些数据结构最有效以及何时/何地使用哪些数据结构。

现在,我可能只是对结构不够了解,但ILookup(of key, ...)Dictionary(of key, list(of ...)) 有何不同?

我还想在哪里使用ILookup?它在程序速度/内存/数据访问等方面会更有效吗?

【问题讨论】:

标签: c# .net vb.net linq


【解决方案1】:

两个显着差异:

  • Lookup 是不可变的。耶 :) (至少,我相信具体的 Lookup 类是不可变的,ILookup 接口不提供任何可变成员。当然,可能还有其他可变实现。)
  • 当您查找一个不存在于查找中的键时,您会得到一个空序列而不是 KeyNotFoundException。 (因此没有TryGetValue,AFAICR。)

它们的效率可能相当 - 例如,查找很可能在幕后使用Dictionary<TKey, GroupingImplementation<TValue>>。根据您的要求在它们之间进行选择。就我个人而言,我发现查找通常比Dictionary<TKey, List<TValue>> 更合适,主要是由于上面的前两点。

请注意,作为实现细节,用于值的IGrouping<,>的具体实现实现IList<TValue>,这意味着与Count()ElementAt()等一起使用是有效的。

【讨论】:

  • 如果不存在的键查找导致空序列而不是异常,那么它非常不能用作通用集合 imo。如果不可变集合是 linq 查询的副产品,这还不错。
  • @nawfal - 这正是查找的用途。来自msdn:“您可以通过在实现 IEnumerable 的对象上调用 ToLookup 来创建 Lookup 的实例。”
【解决方案2】:

有趣的是没有人说出实际最大的不同(直接来自MSDN):

查找类似于字典。这 不同之处在于 Dictionary 将键映射到单个 值,而 Lookup 将键映射到 价值观。

【讨论】:

  • 检查问题:它是关于 Lookup 和 Dictionary> 之间的区别,所以这种区别已经很明显了。
  • @Martao 有些人在谷歌搜索以了解查找和字典之间的区别时发现了这个问题。这个答案真的很有用。
  • @Mladen Mihajlovic,我不明白 MSDN 的解释。字典还可以将键映射到值的集合,例如通过传递一个列表:grouping.ToDictionary(g => g.Key, g => g.ToList())
  • @OfirD 是的,从这个意义上说,它们是相同的。但正如其他答案所述,还有其他差异。
【解决方案3】:

Dictionary<Key, List<Value>>Lookup<Key, Value> 在逻辑上都可以保存以类似方式组织的数据,并且两者的效率顺序相同。主要区别在于 Lookup 是不可变的:它没有 Add() 方法和公共构造函数(正如 Jon 提到的,您可以毫无例外地查询不存在的键,并将键作为分组的一部分)。

至于你用哪个,这真的取决于你想如何使用它们。如果您要维护一个不断修改的多个值的键映射,那么Dictionary<Key, List<Value>> 可能会更好,因为它是可变的。

但是,如果您有一个数据序列,并且只想要按键组织的数据的只读视图,那么查找非常容易构建,并且会为您提供只读快照。

【讨论】:

    【解决方案4】:

    另一个尚未提及的区别是 Lookup() supports null keys:

    Lookup 类实现了 ILookup 接口。查找与字典非常相似,只是允许多个值映射到同一个键,并且支持空键。

    【讨论】:

      【解决方案5】:

      ILookup<K,V>Dictionary<K, List<V>> 之间的主要区别在于字典是可变的;您可以添加或删除键,还可以从查找的列表中添加或删除项目。 ILookup不可变的,一旦创建就不能修改。

      两种机制的底层实现或相同或相似,因此它们的搜索速度和内存占用将大致相同。

      【讨论】:

      • @JohnBus​​tos 在性能方面,没有。这纯粹是合乎逻辑的。您可以传递对结构的引用,而不必担心其他人会从您下面修改它。你可以假设它是不可变的,如果它是可变的,你就不能。
      • 谢谢,Servy,当您经常传递这么多变量 ByRef 时,这是一个非常好的点 - 至少这个您确定不能修改。谢谢!
      • @JohnBus​​tos 请记住,传递方法参数的默认方法是按值传递,您需要显式添加 byref,这是很少应该做的事情。这些数据结构是类,这使得它们成为引用类型,因此传递的值就是引用的值,这就是为什么将其传递给另一个方法会导致调用者可见的变化。
      • 谢谢,Servy,就我一直在做的事情而言,这为我打开了一个全新的蠕虫罐头:),但我确实理解你在说什么。谢谢!!
      • 在幕后你知道 Lookup 是否使用 hashbuckets 作为键吗?
      【解决方案6】:

      当异常不是一个选项时,去查找

      如果您想获得与Dictionary 一样高效的结构,但您不确定输入中没有重复键,则Lookup 更安全。

      正如另一个答案中提到的,它还支持空键,并且在使用任意数据查询时始终返回有效结果,因此它看起来对未知输入更具弹性(比 Dictionary 更不容易引发异常)。

      如果您将其与System.Linq.Enumerable.ToDictionary 函数进行比较,则尤其如此:

      // won't throw
      new[] { 1, 1 }.ToLookup(x => x); 
      
      // System.ArgumentException: An item with the same key has already been added.
      new[] { 1, 1 }.ToDictionary(x => x);
      

      另一种方法是在foreach 循环内编写您自己的重复密钥管理代码。

      性能考虑,字典:明显的赢家

      如果您不需要列表并且要管理大量项目,Dictionary(甚至您自己定制的结构)会更有效:

              Stopwatch stopwatch = new Stopwatch();
              var list = new List<string>();
              for (int i = 0; i < 5000000; ++i)
              {
                  list.Add(i.ToString());
              }
              stopwatch.Start();
              var lookup = list.ToLookup(x => x);
              stopwatch.Stop();
              Console.WriteLine("Creation: " + stopwatch.Elapsed);
      
              // ... Same but for ToDictionary
              var lookup = list.ToDictionary(x => x);
              // ...
      

      由于Lookup 必须为每个键维护一个项目列表,因此它比 Dictionary 慢(对于大量项目,大约慢 3 倍)

      查找速度: 创建:00:00:01.5760444

      字典速度: 创建:00:00:00.4418833

      【讨论】:

      • 我认为这种性能比较是不公平的。对于相同的结果,list.ToLookup(x =&gt; x) 等于 list.GroupBy(x =&gt; x).ToDictionary(group =&gt; group.Key)。因为 Lookup 可以枚举出你一开始说的重复元素。
      • 为了提高性能,从 ILookup 或 Dictionary 中检索会更有趣。典型的用途是只创建一次,并进行频繁的查找。因此,我不会太在意构建它的性能。
      猜你喜欢
      • 2011-12-16
      • 2010-09-28
      • 1970-01-01
      • 1970-01-01
      • 2011-04-08
      • 2019-02-05
      • 1970-01-01
      • 1970-01-01
      • 2015-12-28
      相关资源
      最近更新 更多