添加到Dictionary的不同方式

Sun*_*ers 95 c# optimization dictionary

是什么在差异Dictionary.add(key, value)Dictionary[key] = value

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

编辑:有没有人有权威的信息来源?我尝试过MSDN,但它一如既往的野鹅追逐:(

Ste*_*fen 99

性能几乎完全相同.您可以通过在Reflector.net中打开课程来检查这一点

这是这个索引器:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是Add方法:

public void Add(TKey key, TValue value)
{
    this.Insert(key, value, true);
}
Run Code Online (Sandbox Code Playgroud)

我不会发布整个Insert方法,因为它相当长,但方法声明是这样的:

private void Insert(TKey key, TValue value, bool add)
Run Code Online (Sandbox Code Playgroud)

进一步在功能中,这发生了:

if ((this.entries[i].hashCode == num) && this.comparer.Equals(this.entries[i].key, key))
{
    if (add)
    {
        ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
    }
Run Code Online (Sandbox Code Playgroud)

其中检查密钥是否已存在,如果密钥已存在且参数add为true,则抛出该异常.

因此,出于所有目的和意图,性能是相同的.

像其他一些提到的那样,关键是你是否需要检查,尝试两次添加相同的密钥.

抱歉这篇冗长的帖子,我希望没关系.


hhr*_*avn 67

第一个版本将向字典中添加一个新的KeyValuePair,如果密钥已经在字典中,则抛出.第二个,使用索引器,如果密钥不存在,将添加一个新对,但如果密钥已存在于字典中,则覆盖密钥的值.

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     
Run Code Online (Sandbox Code Playgroud)

  • 不是真的有这样的来源,只是在我的头顶,但我认为它没有比其他评论中提到的更多.如果我没记错的话,Add只使用索引器,但首先检查Key是否已被使用. (3认同)
  • @SuneRievers 我认为,如果接受这个答案,而不是当前接受的答案,这对人们会更有帮助。因为这完全回答了问题(“有什么区别”),而不是公认的问题(谈论性能,并且几乎 99% 搜索此主题的用户(像我一样)需要“差异”(我为什么遇到这个问题)主题),接受的答案对我来说没用,浪费了我们的第二个。相反,这个答案是准确的。 (2认同)

Mic*_*tov 29

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

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

  • 自我描述意图的代码很重要并且非常有价值(并且几乎不需要注释)。这个答案显示了两种方法在意图上的差异,在选择一种方法时应该遵循。换句话说,当您_提前知道_您将始终添加,甚至_必须始终_添加时,请勿使用“索引器添加”。抛出 IndexOutOfBounds 异常比意外行为要好。 (2认同)

net*_*ero 23

要首先回答这个问题,我们需要看看字典和底层技术的目的.

DictionaryKeyValuePair<Tkey, Tvalue>每个值由其唯一键表示的位置的列表.假设我们列出了您最喜欢的食物.每个值(食物名称)由其唯一键表示(位置=您喜欢这种食物的程度).

示例代码:

Dictionary<int, string> myDietFavorites = new Dictionary<int, string>()
{
    { 1, "Burger"},
    { 2, "Fries"},
    { 3, "Donuts"}
};
Run Code Online (Sandbox Code Playgroud)

假设你想要保持健康,你已经改变主意,想要用沙拉代替你最喜欢的"汉堡".您的列表仍然是您的收藏夹列表,您不会更改列表的性质.您最喜欢的将保持在列表中的第一位,只有它的值会改变.这是你打电话的时候:

/*your key stays 1, you only replace the value assigned to this key
  you alter existing record in your dictionary*/
myDietFavorites[1] = "Salad";
Run Code Online (Sandbox Code Playgroud)

但是不要忘记你是程序员,从现在开始你完成你的句子; 你拒绝使用表情符号,因为它们会抛出编译错误,所有收藏列表都是0索引.

你的饮食也改变了!所以你再次改变你的清单:

/*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");
Run Code Online (Sandbox Code Playgroud)

定义有两种可能性,您要么为之前不存在的内容提供新定义,要么想要更改已存在的定义.

Add方法允许您添加记录但仅在一个条件下:您的字典中可能不存在此定义的键.

现在我们来看看幕后.当您创建字典时,编译器会对存储区进行预留(内存中的空格用于存储记录).存储桶不会以您定义它们的方式存储密钥.每个密钥在进入存储桶(由Microsoft定义)之前进行哈希处理,值得一提的是值部分保持不变.

我将使用CRC32哈希算法来简化我的示例.当你定义:

myDietFavorites[0] = "Pizza";
Run Code Online (Sandbox Code Playgroud)

这是怎么回事给水桶db2dc565 "比萨"(简化).

当您使用以下内容更改值时:

myDietFavorites[0] = "Spaghetti";
Run Code Online (Sandbox Code Playgroud)

你将你的0再次作为db2dc565,然后你在桶中查找这个值,看它是否在那里.如果它在那里你只需重写分配给键的值.如果它不在那里你将把你的价值放在桶里.

当您在字典上调用Add函数时,例如:

myDietFavorite.Add(0, "Chocolate");
Run Code Online (Sandbox Code Playgroud)

您对0进行散列以将其值与桶中的值进行比较.只有当它不存在时,您可以将它放在桶中.

了解它是如何工作的至关重要,特别是如果你使用字符串或char类型的键字典.它因为经历散列而区分大小写.例如"name"!="Name".让我们用CRC32来描述这个.

"name"的值为:e04112b1 "Name"的值为:1107fb5b

  • 我假设提出这个问题的人有一定程度的知识或缺乏知识,因此有过度解释。 (2认同)

Guf*_*ffa 5

是的,这就是区别,如果键已经存在,Add 方法会抛出异常。

使用Add方法的原因正是如此。如果字典不应该包含该键,那么您通常需要例外,以便您意识到问题。