无法直接向字典中添加键值对。

50

我想向一个Dictionary<T, U>添加一个KeyValuePair<T,U>,但是我无法这样做。我必须分别传递键和值,这意味着Add方法必须创建一个新的KeyValuePair对象来插入,这可能不是很有效率。我无法相信Add方法上没有Add(KeyValuePair<T, U>)重载。有人能为这个明显的疏忽提出可能的原因吗?


1
没有这种重载是因为字典不仅仅是“插入”一个新的键值对。(我甚至怀疑在底层它是否以那种格式存储)它会测试键是否已经存在,并可能执行一些操作将其放置在正确的位置,以保持查询尽可能快速。 - LightStriker
1
你可以直接进行赋值操作:dictionary[key] = value; - jwaliszko
你的键和值是什么类型? - Jacek
我认为你可以忽略“Add”方法的开销。 - Felix K.
对我来说,这似乎是一个合理的问题,撇开“效率”不谈,将KVP分离出来添加是很烦人的。 - nicodemus13
显示剩余2条评论
8个回答

50

您可以使用 IDictionary<TKey,TValue> 接口,该接口提供了 Add(KeyValuePair<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"));

9
请注意,该方法只是调用了 Add(kvp.Key, kvp.Value) - Rawling
2
@Rawling 是的,在大多数情况下是正确的,但这取决于 IDictionary 接口的实现方式(例如,Sorted Dictionary 的实现方式与调用 Add(kvp.Key,kvp.Value) 不同)。 - DmitryG

40

备份一下...在进行优化之前,您应该确定创建新的KeyValuePair是否真的效率低下。

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

要注意的第一件事是KeyValuePair是一个结构体。这意味着它必须从堆栈复制到堆中,以便作为方法参数传递。当KeyValuePair添加到字典中时,它必须再次复制以确保值类型语义。

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


1
+1 正是我想的。但我太懒了,没写下来。 - Felix K.
“不是内部实现为一组键/值对,而是一堆数组”:实际上并不准确。有一个私有成员结构体Dictionary<K, T>.Entry的数组(包含键、值和一些整数)。但除此之外,您的想法是正确的(但未提及“过早优化”:-))。 - Richard
字典类在内部使用桶数组和条目数组来分离项目。你说“一堆”可能有点言过其实。 - Tim Copenhaver
1
复制 KeyValuePair<Of TKey,TValue> 的成本与分别复制 TKeyTValue 差不多。构造这样的结构体来调用 Add 方法并没有什么帮助,但如果手动枚举 IEnumerator<KeyValuePair<TKey,TValue>>,则调用 Add(myEnumerator.Current);Add(myEnumerator.Current.Key,myEnumerator.Current.Value); 更有效率。我认为公开重载方法不会有任何问题。 - supercat
说“字典内部没有实现KeyValuePairs”至少是一半错误的。请参见https://dev59.com/wWcs5IYBdhLWcg3wPxnN#13012353 此外,效率讨论是无意义的,因为显式接口通过添加kvp对象引用并带有一些小开销来强制执行唯一键哈希。 - FizxMike

18

有这样一个方法 ICollection<KeyValuePair<K, T>>.Add,但由于它是显式实现,所以需要将您的字典对象转换为该接口才能访问它。

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

请查看:

此方法的页面中包含示例。


1
你是指 ((ICollection<KeyValuePair<KeyType, ValueType>>)myDict).Add(myPair); 吗? - nicodemus13
非常感谢@Richard提供明智(和正确)的答案!说真的,被采纳的答案偏离了轨道,至少有一半是错误的。SMH。 - FizxMike

3
如果有人确实想要这样做,这里有一个扩展程序。
    public static void Add<T, U>(this IDictionary<T, U> dic, KeyValuePair<T, U> KVP)
    {
        dic.Add(KVP.Key, KVP.Value);
    }

但是如果没有真正的必要,我建议不要这样做。

2
除非我弄错了,.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"));

    ...
}

可以看到,创建了一个名为openWith的新字典类型对象。然后使用.Add方法创建并将一个新的KVP对象添加到openWith中。

1
openWith 被声明为 ICollection<KeyValuePair<String, String>>,尽管它在内部使用了一个 Dictionary<string, string>。这就是为什么 Add() 方法可用的原因。 - asherber

1

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

如果您确实需要传递KVP,因为您已经以该格式获取了它们,请使用IDictionary。否则,请使用赋值或者直接使用Add方法。


0

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

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);
        }
    }
}

-2

我不是百分之百确定,但我认为字典的内部实现是哈希表,这意味着键被转换为哈希以执行快速查找。

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

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


1
虽然正确,但与问题毫无关系。所以评分-1。(即使哈希表需要存储原始值来处理冲突并枚举键。) - Richard
这个问题显然源于不理解字典的内部工作原理。我的回答让他能够自己回答这个问题以及进一步的问题,或者至少知道在哪里寻找答案。 - Gertjan Assies
1
我并不完全同意。是的,有一些关于内部表示的假设,但实际上它们比你对内部表示的假设更接近(它是一个键、值和一对整数的结构体数组)。最后,回答的开头应该得到-1:阅读类型的MSDN页面上的备注,明确说明它是一个哈希表。 - Richard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接