Incrementing a numerical value in a dictionary
I'm using the code below to either increment or insert a value in a dictionary. If the key I'm incrementing doesn't exist I'd like to set its value to 1.
public void IncrementCount(Dictionary<int, int> someDictionary, int id)
{开发者_如何学Python
int currentCount;
if (someDictionary.TryGetValue(id, out currentCount))
{
someDictionary[id] = currentCount + 1;
}
else
{
someDictionary[id] = 1;
}
}
Is this an appropriate way of doing so?
Your code is fine. But here's a way to simplify in a way that doesn't require branching in your code:
int currentCount;
// currentCount will be zero if the key id doesn't exist..
someDictionary.TryGetValue(id, out currentCount);
someDictionary[id] = currentCount + 1;
This relies on the fact that the TryGetValue
method sets value
to the default value of its type if the key doesn't exist. In your case, the default value of int
is 0
, which is exactly what you want.
UPD. Starting from C# 7.0 this snippet can be shortened using out variables
:
// declare variable right where it's passed
someDictionary.TryGetValue(id, out var currentCount);
someDictionary[id] = currentCount + 1;
As it turns out it made sense to use the ConcurrentDictionary which has the handy upsert method: AddOrUpdate.
So, I just used:
someDictionary.AddOrUpdate(id, 1, (id, count) => count + 1);
Here's a nice extension method:
public static void Increment<T>(this Dictionary<T, int> dictionary, T key)
{
int count;
dictionary.TryGetValue(key, out count);
dictionary[key] = count + 1;
}
Usage:
var dictionary = new Dictionary<string, int>();
dictionary.Increment("hello");
dictionary.Increment("hello");
dictionary.Increment("world");
Assert.AreEqual(2, dictionary["hello"]);
Assert.AreEqual(1, dictionary["world"]);
It is readable and the intent is clear. I think this is fine. No need to invent some smarter or shorter code; if it doesn't keep the intent just as clear as your initial version :-)
That being said, here is a slightly shorter version:
public void IncrementCount(Dictionary<int, int> someDictionary, int id)
{
if (!someDictionary.ContainsKey(id))
someDictionary[id] = 0;
someDictionary[id]++;
}
If you have concurrent access to the dictionary, remember to synchronize access to it.
Just some measurements on .NET 4 for integer keys.
It's not quite an answer to your question, but for the sake of completeness I've measured the behavior of various classes useful for incrementing integers based on integer keys: simple Array
, Dictionary
(@Ani's approach), Dictionary
(simple approach), SortedDictionary
(@Ani's approach) and ConcurrentDictionary.TryAddOrUpdate
.
Here is the results, adjusted by 2.5 ns for wrapping with classes instead of direct usage:
Array 2.5 ns/inc
Dictionary (@Ani) 27.5 ns/inc
Dictionary (Simple) 37.4 ns/inc
SortedDictionary 192.5 ns/inc
ConcurrentDictionary 79.7 ns/inc
And that's the code.
Note that ConcurrentDictionary.TryAddOrUpdate
is three times slower than Dictionary
's TryGetValue
+ indexer's setter. And the latter is ten times slower than Array.
So I would use an array if I know the range of keys is small and a combined approach otherwise.
Here is a handy unit test for you to play with concerning ConcurrentDictionary and how to keep the values threadsafe:
ConcurrentDictionary<string, int> TestDict = new ConcurrentDictionary<string,int>();
[TestMethod]
public void WorkingWithConcurrentDictionary()
{
//If Test doesn't exist in the dictionary it will be added with a value of 0
TestDict.AddOrUpdate("Test", 0, (OldKey, OldValue) => OldValue+1);
//This will increment the test key value by 1
TestDict.AddOrUpdate("Test", 0, (OldKey, OldValue) => OldValue+1);
Assert.IsTrue(TestDict["Test"] == 1);
//This will increment it again
TestDict.AddOrUpdate("Test", 0, (OldKey, OldValue) => OldValue+1);
Assert.IsTrue(TestDict["Test"] == 2);
//This is a handy way of getting a value from the dictionary in a thread safe manner
//It would set the Test key to 0 if it didn't already exist in the dictionary
Assert.IsTrue(TestDict.GetOrAdd("Test", 0) == 2);
//This will decriment the Test Key by one
TestDict.AddOrUpdate("Test", 0, (OldKey, OldValue) => OldValue-1);
Assert.IsTrue(TestDict["Test"] == 1);
}
精彩评论