.net: Adding dictionary item - check if it exists or allow exception?

I would do the Contains check.

My reasoning is exceptions should be saved for those things that just shouldn't happen. If they do then alarm bells should be rang and calvery brought in. Just seems odd to me to use exceptions for known issue case handling especially when you can test for it.


How to approach this depends on what you want to do if a collision happens. If you want to keep the first inserted value, you should use ContainsKey to check before inserting. If, on the other hand, you want to use the last value for that key, you can do like so:

// c# sample:
myDictionary[key] = value;

As a side note: I would probably, if possible, use Dictionary<string, string> instead of StringDictionary. If nothing else that will give you access to some more Linq extension methods.


I did some benchmarking regarding this. But I have to reiterate Kelsey's point:

exceptions should be saved for those things that just shouldn't happen. If they do then alarm bells should be rang and calvary brought in. Just seems odd to me to use exceptions for known issue case handling especially when you can test for it.

It makes sense because the performance gain you gain by going for try-catch (if at all) is trivial but the "catch" can be more penalizing. Here's the test:

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

No duplicates:

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

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

    }
}, 100000);  

50% duplicates:

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

100% duplicates

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

Results:

No duplicates

approach 1: debug -> 630 ms - 680 ms; release -> 620 ms - 640 ms

approach 2: debug -> 640 ms - 690 ms; release -> 640 ms - 670 ms

50% duplicates

approach 1: debug -> 26 ms - 39 ms; release -> 25 ms - 33 ms

approach 2: debug -> 1340 ms; release -> 1260 ms

100% duplicates

approach 1: debug -> 7 ms; release -> 7 ms

approach 2: debug -> 2600 ms; release -> 2400 ms

You can see that as duplicates increase, try-catch performs poorly. Even in the worst case where you have no duplicates at all, the performance gain of try-catch isn't anything substantial.


If at all possible, replace StringDictionary with Dictionary<string, string>, and use TryGetValue. This avoids both exception handling overhead, and double lookup.