Different ways of adding to Dictionary
我注意到,在插入重复的密钥时,最后一个版本不会抛出一个
编辑:是否有人对此有权威的信息来源?我试过MSDN,但它一如既往地是一场疯狂的追逐:(
性能几乎完全相同。您可以通过在reflector.net中打开类来检查这一点。
这是这个索引器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public TValue this[TKey key] { get { int index = this.FindEntry(key); if (index >= 0) { return this.entries[index].value; } ThrowHelper.ThrowKeyNotFoundException(); return default(TValue); } set { this.Insert(key, value, false); } } |
这是添加方法:
1 2 3 4 | public void Add(TKey key, TValue value) { this.Insert(key, value, true); } |
我不会发布整个insert方法,因为它很长,但是方法声明如下:
1 | private void Insert(TKey key, TValue value, bool add) |
在函数的更深处,会发生以下情况:
1 2 3 4 5 6 | if ((this.entries[i].hashCode == num) && this.comparer.Equals(this.entries[i].key, key)) { if (add) { ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate); } |
它检查键是否已经存在,如果已经存在并且参数add为true,则抛出异常。
因此,无论出于何种目的和意图,表演都是一样的。
像其他几次提到的一样,这都是关于您是否需要检查,是否尝试添加同一个密钥两次。
对不起,这篇冗长的文章,我希望没问题。
第一个版本将向字典中添加一个新的keyValuePair,如果密钥已经在字典中,则抛出。第二种方法是使用索引器,如果键不存在,则添加一个新的对;如果键已经存在于字典中,则覆盖它的值。
1 2 3 4 5 | IDictionary<string, string> strings = new Dictionary<string, string>(); strings["foo"] ="bar"; //strings["foo"] =="bar" strings["foo"] = string.Empty; //strings["foo"] == string.empty strings.Add("foo","bar"); //throws |
- 使用
Add 方法添加新的键/值对,不会替换现有的键(抛出ArgumentException )。 - 如果不关心键是否已存在于字典中,则使用索引器,换句话说:如果键不在字典中,则添加键/值对;如果键已在字典中,则替换指定键的值。
要首先回答这个问题,我们需要了解字典的用途和底层技术。
示例代码:
1 2 3 4 5 6 | Dictionary<int, string> myDietFavorites = new Dictionary<int, string>() { { 1,"Burger"}, { 2,"Fries"}, { 3,"Donuts"} }; |
假设你想保持健康,你改变了主意,你想用沙拉代替你最喜欢的汉堡。您的列表仍然是您最喜爱的列表,您不会更改列表的性质。您的最爱将保持在列表中的第一位,只有它的值会改变。这就是你所说的:
1 2 3 | /*your key stays 1, you only replace the value assigned to this key you alter existing record in your dictionary*/ myDietFavorites[1] ="Salad"; |
但是不要忘记你是程序员,从现在起你用完成句子;你拒绝使用emojis,因为它们会抛出编译错误,并且所有的收藏夹列表都是基于0索引的。
你的饮食也变了!所以你再次修改你的列表:
1 2 3 4 5 6 | /*you don't want to replace Salad, you want to add this new fancy 0 position to your list. It wasn't there before so you can either define it*/ myDietFavorites[0] ="Pizza"; /*or Add it*/ myDietFavorites.Add(0,"Pizza"); |
定义有两种可能性,您要么想要为以前不存在的东西给出一个新的定义,要么想要更改已经存在的定义。
添加方法允许您添加记录,但只能在一个条件下添加:字典中可能不存在此定义的键。
现在我们要看看引擎盖下面。当你编一本字典时,你的编译器会为存储桶(内存中存储记录的空间)做一个预留。bucket不会按照您定义的方式存储密钥。在进入bucket(由Microsoft定义)之前,每个键都被散列,值得一提的是值部分保持不变。
我将使用CRC32哈希算法来简化我的示例。定义时:
1 | myDietFavorites[0] ="Pizza"; |
桶里装的是DB2DC565"比萨"(简化版)。
更改中的值时使用:
1 | myDietFavorites[0] ="Spaghetti"; |
散列0,它又是db2dc565,然后在bucket中查找该值,以确定它是否存在。如果存在,只需重写分配给键的值。如果它不在那里,你将把你的价值放在桶里。
在字典上调用add函数时,如:
1 | myDietFavorite.Add(0,"Chocolate"); |
您散列0以将其值与桶中的值进行比较。只有当它不在桶里时,你才可以把它放在桶里。
了解它是如何工作的是非常重要的,特别是当您使用字符串或字符类型的键的字典时。它对大小写敏感,因为正在进行哈希运算。例如"name"!="姓名"。让我们用CRC32来描述这个。
"名称"的值为:e04112b1"名称"的值为:1107FB5B
是的,这就是区别,如果键已经存在,则add方法将抛出异常。
使用add方法的原因正是如此。如果字典不应该已经包含键,那么您通常需要异常,这样您就可以知道问题了。
一个是赋值,另一个是向字典添加新的键和值。
考虑到性能上最可能的相似性,可以使用对您所使用的代码段感觉更正确和可读的任何代码。
我觉得一个描述一个加法的操作,作为已经存在的键,一个非常罕见的异常最好用加法来表示。从语义上讲,它更有意义。