关于C#:添加到字典的不同方式

Different ways of adding to Dictionary

Dictionary.add(key, value)Dictionary[key] = value有什么区别?

我注意到,在插入重复的密钥时,最后一个版本不会抛出一个ArgumentException,但是否有理由更喜欢第一个版本?

编辑:是否有人对此有权威的信息来源?我试过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


Dictionary.Add(key, value)Dictionary[key] = value有不同的用途:

  • 使用Add方法添加新的键/值对,不会替换现有的键(抛出ArgumentException)。
  • 如果不关心键是否已存在于字典中,则使用索引器,换句话说:如果键不在字典中,则添加键/值对;如果键已在字典中,则替换指定键的值。


要首先回答这个问题,我们需要了解字典的用途和底层技术。

DictionaryKeyValuePair的列表,其中每个值都由其唯一键表示。假设我们有你最喜欢的食物的清单。每个值(食品名称)都由其唯一键(位置=您对该食品的喜爱程度)表示。

示例代码:

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方法的原因正是如此。如果字典不应该已经包含键,那么您通常需要异常,这样您就可以知道问题了。


一个是赋值,另一个是向字典添加新的键和值。


考虑到性能上最可能的相似性,可以使用对您所使用的代码段感觉更正确和可读的任何代码。

我觉得一个描述一个加法的操作,作为已经存在的键,一个非常罕见的异常最好用加法来表示。从语义上讲,它更有意义。

dict[key] = value代表了更好的替代。如果我看到那段代码,我有一半的期望密钥已经在字典里了。