.net:添加字典项 - 检查它是否存在或允许异常?

Sco*_*ttE 15 .net dictionary

我正在向StringDictionary添加项目,并且可能会出现重复的密钥.这当然会抛出异常.

如果重复的可能性非常低(即很少发生),我最好使用Try Catch块并使其处理不当,或者在添加每个条目之前是否应该总是进行.ContainsKey检查?

我假设如果重复密钥的可能性很高,那么允许异常将是一个糟糕的决定,因为它们很昂贵.

思考?

编辑

我在泛型字典中使用了反射器,并为ContainsKey和TryGetValue找到了以下内容,因为两者都在下面提到.

public bool TryGetValue(TKey key, out TValue value)
{
    int index = this.FindEntry(key);
    if (index >= 0)
    {
        value = this.entries[index].value;
        return true;
    }
    value = default(TValue);
    return false;
}
Run Code Online (Sandbox Code Playgroud)

public bool ContainsKey(TKey key)
{
    return (this.FindEntry(key) >= 0);
}
Run Code Online (Sandbox Code Playgroud)

我错过了什么,或者TryGetValue比ContainsKey做更多的工作?


我很欣赏这些回复,对于我目前的目的,我将继续做一个ContainsKey调用,因为集合很小,而且代码更具可读性.

Fre*_*örk 16

如何处理这取决于发生碰撞时您想要做什么.如果要保留第一个插入的值,则应ContainsKey在插入之前进行检查.另一方面,如果要使用该键的最后一个值,则可以这样做:

// c# sample:
myDictionary[key] = value;
Run Code Online (Sandbox Code Playgroud)

作为旁注:如果可能的话,我可能会使用Dictionary<string, string>而不是StringDictionary.如果没有别的东西可以让你访问更多的Linq扩展方法.


Kel*_*sey 7

我会做包含检查.

我的理由是应该为那些不应该发生的事情保存例外.如果他们这样做,那么应该响起警报铃声并引入调用.对于我来说,对于已知问题案例处理使用例外情况似乎很奇怪,尤其是当您可以测试它时.


naw*_*fal 6

我对此做了一些基准测试。但我必须重申凯尔西的观点:

应该为那些不应该发生的事情保存异常。如果他们这样做了,那么应该敲响警钟并引入髑髅。对我来说,使用异常处理已知问题案例似乎很奇怪,尤其是当您可以对其进行测试时。

这是有道理的,因为你通过追求try-catch(如果有的话)获得的性能提升是微不足道的,但“捕获”可能会更加不利。这是测试:

public static void Benchmark(Action method, int iterations = 10000)
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    for (int i = 0; i < iterations; i++)
        method();

    sw.Stop();
    MessageBox.Show(sw.Elapsed.TotalMilliseconds.ToString());
}

public static string GetRandomAlphaNumeric()
{
    return Path.GetRandomFileName().Replace(".", "").Substring(0, 8);
}

var dict = new Dictionary<string, int>();
Run Code Online (Sandbox Code Playgroud)

没有重复:

Benchmark(() =>
{
    // approach 1
    var key = GetRandomAlphaNumeric();
    if (!dict.ContainsKey(key))
        dict.Add(item, 0);

    // approach 2
    try
    {
        dict.Add(GetRandomAlphaNumeric(), 0);
    }
    catch (ArgumentException)
    {

    }
}, 100000);  
Run Code Online (Sandbox Code Playgroud)

50% 重复:

for (int i = 0; i < 50000; i++)
{
    dict.Add(GetRandomAlphaNumeric(), 0);  
}

var lst = new List<string>();
for (int i = 0; i < 50000; i++)
{
    lst.Add(GetRandomAlphaNumeric());
}
lst.AddRange(dict.Keys);
Benchmark(() =>
{
    foreach (var key in lst)
    {
        // approach 1
        if (!dict.ContainsKey(key))
            dict.Add(key, 0);

        // approach 2
        try
        {
            dict.Add(key, 0);
        }
        catch (ArgumentException)
        {

        }
    }
}, 1);  
Run Code Online (Sandbox Code Playgroud)

100% 重复

var key = GetRandomAlphaNumeric();
dict.Add(key, 0);
Benchmark(() =>
{
    // approach 1
    if (!dict.ContainsKey(key))
        dict.Add(item, 0);

    // approach 2
    try
    {
        dict.Add(key, 0);
    }
    catch (ArgumentException)
    {

    }
}, 100000);
Run Code Online (Sandbox Code Playgroud)

结果

没有重复

方法 1:调试 -> 630 毫秒 - 680 毫秒;释放 -> 620 毫秒 - 640 毫秒

方法 2:调试 -> 640 毫秒 - 690 毫秒;释放 -> 640 毫秒 - 670 毫秒

50% 重复

方法 1:调试 -> 26 毫秒 - 39 毫秒;释放 -> 25 毫秒 - 33 毫秒

方法 2:调试 -> 1340 毫秒;释放 -> 1260 毫秒

100% 重复

方法 1:调试 -> 7 毫秒;释放 -> 7 毫秒

方法 2:调试 -> 2600 毫秒;释放 -> 2400 毫秒

您可以看到,随着重复次数的增加,try-catch表现不佳。即使在根本没有重复项的最坏情况下, 的性能增益try-catch也没有任何实质性的提升。


Pav*_*aev 5

如果可能的话,更换StringDictionaryDictionary<string, string>,并使用TryGetValue.这避免了异常处理开销和双重查找.

  • TryGetValue()在其实现中执行双重查找......并且它不是线程安全的 (2认同)

iSp*_*n17 5

对于 .NET Standard 2.1+ 和 .NET Core 2.0+来说,这个问题要简单得多。只要使用TryAdd方法就可以了。它以最优雅的方式处理您遇到的所有问题。