Dirk Boer
Dirk Boer

Reputation: 9153

Dictionary Add/Set in one call for performance reasons

In a Dictionary<struct,int>: is it possible to Add/Set in one call?

So is it possible to do the code below in only one lookup per entry?

_KeyToPoints = new Dictionary<Key,int>();

foreach ( var entry in billionEntries )
{
    int originalValue;

    // first lookup
    _KeyToPoints.TryGetValue(entry.Key, out originalValue);

    // second lookup
    _KeyToPoints[key] = originalValue + points;    
}

This is done in a very tight loop over a huge amount of data, so all performance matters.

Or is there a better suitable data structure?

Upvotes: 7

Views: 1162

Answers (4)

MSE
MSE

Reputation: 345

You can use the old Hashtable container :

Hashtable map = new Hashtable();
map[key] = value;

If the key does not exists, it will create and add it automatically to the map. But you will suffer from the boxing/unboxing if you use a key that is a value type...

Upvotes: -1

astidham2003
astidham2003

Reputation: 964

Roll your own Dictionary. If all you really need is an optimized setter/getter function for a specific type, then you can make your own Dictionary specialized for the purpose. The source code for the built-in Dictionary can be found here http://referencesource.microsoft.com/#mscorlib/system/collections/generic/dictionary.cs,d3599058f8d79be0 if you need some reference code to get you thinking.

Upvotes: 1

Quality Catalyst
Quality Catalyst

Reputation: 6793

Very nasty solution, but it will be faster under the assumption you call the AddPoints() method mostly for already existing items in the dictionary.

void AddPoints(T key, int points)
{
    try
    {
        _KeyToPoints[key] = _KeyToPoints[key] + points;
    }
    catch (ArgumentException)
    {
        _KeyToPoints[key] = points;
    }
}

Throwing and handling exceptions is very expensive (time consuming) and will impact your performance negatively. To avoid this, you can pre-fill the dictionary like this:

void PreFillKeys(params T[] keys) // use an IEnumerable<T> if handier
{
    foreach (T key in keys)
    {
        // EITHER THIS:
        if (!_KeyToPoints.ContainsKey(key))
            _KeyToPoints[key] = 0;
        /* OR THIS (faster if you know none of keys was added before)
        try
        {
            _KeyToPoints[key] = 0;
        }
        catch (ArgumentException)
        {
            // ignore >> you shouldn't have called 
        }
        */
    }
}

Upvotes: -2

Timothy Shields
Timothy Shields

Reputation: 79631

Yes, there is a way to do this, but it has a downside. Consider this class:

class Ref<T>
{
    public T Value;
}

You can use a Dictionary<K, Ref<int>> dict and then do this:

Ref<int> count;
if (!dict.TryGetValue(key, out count))
{
    count = new Ref<int> { Value = 0 };
    dict[key] = count;
}
count.Value += points;

The downside is that now you have an additional heap object per entry in your dictionary. Depending on your situation, this may or may not be acceptable.

Upvotes: 3

Related Questions