【问题标题】:Can't add keyValuePair directly to Dictionary无法将 keyValuePair 直接添加到 Dictionary
【发布时间】:2012-10-12 07:28:54
【问题描述】:

我想将KeyValuePair<T,U> 添加到Dictionary<T, U>,但我不能。我必须分别传递key和value,这意味着Add方法必须创建一个新的KeyValuePair对象来插入,效率不高。我不敢相信 Add 方法上没有 Add(KeyValuePair<T, U>) 重载。任何人都可以提出这种明显疏忽的可能原因吗?

【问题讨论】:

  • 不存在这种重载,因为字典不仅“插入”新的 KeyValuePair。 (我什至怀疑它是否以这种格式存储在引擎盖下)它测试密钥是否已经存在并可能执行一些操作以将其放置在正确的位置以尽可能快地保持查询。
  • 你可以只做一个赋值:dictionary[key] = value;
  • 你的键和值的类型是什么?
  • 我认为您可以忽略“添加”方法的开销。
  • 也可以使用dictionary.Add(pair.key, pair.Value),其中pair是KVP对象

标签: c# dictionary key-value generic-collections


【解决方案1】:

您可以使用提供Add(KeyValuePair<TKey,TValue>)方法的IDictionary<TKey,TValue>接口:

IDictionary<int, string> dictionary = new Dictionary<int, string>();
dictionary.Add(new KeyValuePair<int,string>(0,"0"));
dictionary.Add(new KeyValuePair<int,string>(1,"1"));

【讨论】:

  • 还要注意这个方法只是调用Add(kvp.Key, kvp.Value)
  • @Rawling 是的,在大多数情况下确实如此,但这取决于 IDictionary 接口的实现(例如,排序字典以其他方式实现它而不是调用 Add(kvp.Key,kvp.Value ))
【解决方案2】:

备份一分钟...在进行监督之前,您应该确定创建新的 KeyValuePair 是否真的如此低效。

首先,Dictionary 类不是在内部实现为一组键/值对,而是作为一组数组。除此之外,让我们假设它只是一组 KeyValuePairs 并查看效率。

首先要注意的是KeyValuePair 是一个结构。真正的含义是它必须从堆栈复制到堆才能作为方法参数传递。当 KeyValuePair 添加到字典中时,必须再次复制它以确保值类型语义。

为了将Key和Value作为参数传递,每个参数可以是值类型,也可以是引用类型。如果它们是值类型,则性能将与 KeyValuePair 路由非常相似。如果它们是引用类型,这实际上可以是一个更快的实现,因为只需要传递地址并且几乎不需要进行复制。在最好的情况和最坏的情况下,由于 KeyValuePair 结构本身的开销增加,此选项比 KeyValuePair 选项略好。

【讨论】:

  • +1 正是我的想法。但我一直懒得写下来。
  • “内部不是作为一组键/值对实现的,而是作为一组数组”:实际上并非如此。私有成员结构Dictionary&lt;K, T&gt;.Entry(键、值和几个整数)有 一个 数组。但否则你就在正确的轨道上(但没有提到 *premature optimisation" :-))。
  • Dictionary 类在内部使用一个桶数组和一个条目数组来分离项目。不过,你说得对,“一堆”可能有点夸大其词。
  • 复制KeyValuePair&lt;Of TKey,TValue&gt;) 的成本与分别复制TKeyTValue 的成本大致相同。为调用Add 方法而构建这样一个结构不会有帮助,但如果手动枚举IEnumerator&lt;KeyValuePair&lt;TKey,TValue&gt;&gt;,调用Add(myEnumerator.Current); 将比Add(myEnumerator.Current.Key,myEnumerator.Current.Value); 更有效我不认为公开过载会伤害任何东西。
  • 说“字典没有在内部实现 KeyValuePairs”至少有一半是错误的。请参阅stackoverflow.com/a/13012353/1297040 此外,效率讨论是无稽之谈,因为显式接口添加了 kvp 对象引用,并带有一些小开销来强制唯一键散列。
【解决方案3】:

有这样一种方法——ICollection&lt;KeyValuePair&lt;K, T&gt;&gt;.Add,但由于它是显式实现的,您需要将字典对象转换为该接口才能访问它。

((ICollection<KeyValuePair<KeyType, ValueType>>)myDict).Add(myPair);

此方法上的page 包含一个示例。

【讨论】:

  • 你的意思是((ICollection&lt;KeyValuePair&lt;KeyType, ValueType&gt;&gt;)myDict).Add(myPair);
  • 非常感谢@Richard 的理智(和正确)回答!说真的,接受的答案偏离了轨道,至少有一半是错误的 SMH。
【解决方案4】:

除非我弄错了,.NET 4.5 和 4.6 添加了将 KeyValuePair 添加到字典的功能。 (如果我错了,请通知我,我会删除这个答案。)

https://msdn.microsoft.com/en-us/library/cc673027%28v=vs.110%29.aspx

从上面的链接,相关的信息是这个代码示例:

public static void Main() 
{
    // Create a new dictionary of strings, with string keys, and 
    // access it through the generic ICollection interface. The 
    // generic ICollection interface views the dictionary as a 
    // collection of KeyValuePair objects with the same type 
    // arguments as the dictionary. 
    //
    ICollection<KeyValuePair<String, String>> openWith =
        new Dictionary<String, String>();

    // Add some elements to the dictionary. When elements are  
    // added through the ICollection<T> interface, the keys 
    // and values must be wrapped in KeyValuePair objects. 
    //
    openWith.Add(new KeyValuePair<String,String>("txt", "notepad.exe"));
    openWith.Add(new KeyValuePair<String,String>("bmp", "paint.exe"));
    openWith.Add(new KeyValuePair<String,String>("dib", "paint.exe"));
    openWith.Add(new KeyValuePair<String,String>("rtf", "wordpad.exe"));

    ...
}

可以看出,一个 Dictionary 类型的新对象被创建并命名为openWith。然后使用.Add 方法创建一个新的KVP 对象并添加到openWith

【讨论】:

  • openWith 被声明为ICollection&lt;KeyValuePair&lt;String, String&gt;&gt;,即使它在后台使用Dictionary&lt;string, string&gt;。这就是Add() 可用的原因。
【解决方案5】:

如果有人真的想这样做,这里有一个扩展

    public static void Add<T, U>(this IDictionary<T, U> dic, KeyValuePair<T, U> KVP)
    {
        dic.Add(KVP.Key, KVP.Value);
    }

但如果没有真正需要这样做,我建议不要这样做

【讨论】:

    【解决方案6】:

    仅仅因为 Dictionary 类的枚举器返回一个 KeyValuePair,并不意味着它在内部是这样实现的。

    如果您确实需要通过 KVP,请使用 IDictionary,因为您已经获得了该格式的 KVP。否则使用赋值或只使用 Add 方法。

    【讨论】:

      【解决方案7】:

      将它作为扩展添加到您的项目中会有什么问题?

      namespace System.Collection.Generic
      {
          public static class DictionaryExtensions
          {
              public static void AddKeyValuePair<K,V>(this IDictionary<K, V> me, KeyValuePair<K, V> other)
              {
                  me.Add(other.Key, other.Value);
              }
          }
      }
      

      【讨论】:

        【解决方案8】:

        我不是 100% 确定,但我认为 Dictionary 的内部实现是一个哈希表,这意味着键被转换为哈希以执行快速查找。

        如果您想了解有关哈希表的更多信息,请阅读此处

        http://en.wikipedia.org/wiki/Hash_table

        【讨论】:

        • 虽然正确,但这与问题无关。所以-1。 (即使是哈希表也需要存储原始值来处理冲突以及键的枚举。)
        • 这个问题显然源于对字典的内部工作缺乏了解。我的回答使他能够自己回答这个问题和进一步的问题,或者至少可以在哪里寻找答案。
        • 我不完全同意。是的,有关于内部表示的假设,但实际上它们更接近于您对内部表示的假设(它是键、值和几个整数的单个结构数组)。最后,答案的开头值得 -1:阅读 MSDN 页面上关于类型的注释,明确声明它是一个哈希表。
        猜你喜欢
        • 2010-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-05
        • 1970-01-01
        • 2017-04-04
        相关资源
        最近更新 更多