Ответ 1
Как оказалось, имеет смысл использовать ConcurrentDictionary, который имеет удобный метод upsert: AddOrUpdate.
Итак, я просто использовал:
someDictionary.AddOrUpdate(id, 1, (id, count) => count + 1);
Я использую приведенный ниже код для увеличения или вставки значения в словаре. Если ключ, который я увеличиваю, не существует, я хотел бы установить его значение равным 1.
public void IncrementCount(Dictionary<int, int> someDictionary, int id)
{
int currentCount;
if (someDictionary.TryGetValue(id, out currentCount))
{
someDictionary[id] = currentCount + 1;
}
else
{
someDictionary[id] = 1;
}
}
Это подходящий способ сделать это?
Как оказалось, имеет смысл использовать ConcurrentDictionary, который имеет удобный метод upsert: AddOrUpdate.
Итак, я просто использовал:
someDictionary.AddOrUpdate(id, 1, (id, count) => count + 1);
Ваш код в порядке. Но здесь можно упростить путь, который не требует разветвления в вашем коде:
int currentCount;
// currentCount will be zero if the key id doesn't exist..
someDictionary.TryGetValue(id, out currentCount);
someDictionary[id] = currentCount + 1;
Это зависит от того, что метод TryGetValue
устанавливает значение value
в значение по умолчанию его типа, если ключ doesn ' t существует. В вашем случае значение по умолчанию int
равно 0
, что именно то, что вы хотите.
UPD. Начиная с С# 7.0 этот фрагмент можно сократить с помощью out variables
:
// declare variable right where it passed
someDictionary.TryGetValue(id, out var currentCount);
someDictionary[id] = currentCount + 1;
Вот хороший метод расширения:
public static void Increment<T>(this Dictionary<T, int> dictionary, T key)
{
int count;
dictionary.TryGetValue(key, out count);
dictionary[key] = count + 1;
}
Использование:
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"]);
Это читаемо, и цель понятна. Думаю, все в порядке. Не нужно изобретать более умный или более короткий код; если он не сохраняет намерение так же ясно, как ваша первоначальная версия:-)
Говоря это, немного более короткая версия:
public void IncrementCount(Dictionary<int, int> someDictionary, int id)
{
if (!someDictionary.ContainsKey(id))
someDictionary[id] = 0;
someDictionary[id]++;
}
Если у вас есть параллельный доступ к словарю, не забудьте синхронизировать с ним доступ.
Просто некоторые измерения на .NET 4 для целых ключей.
Это не совсем ответ на ваш вопрос, но для полноты я измерил поведение различных классов, полезных для приращения целых чисел на основе целых ключей: simple Array
, Dictionary
(подход @Ani), Dictionary
(простой подход), SortedDictionary
(подход @Ani) и ConcurrentDictionary.TryAddOrUpdate
.
Вот результаты, скорректированные на 2,5 нс для упаковки классами вместо прямого использования:
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
И что код.
Обратите внимание, что ConcurrentDictionary.TryAddOrUpdate
в три раза медленнее, чем Dictionary
TryGetValue
+ установщик индексатора. И последнее в десять раз медленнее, чем Array.
Итак, я бы использовал массив, если бы знал, что диапазон ключей мал, и комбинированный подход в противном случае.
Вот удобный unit test для вас, чтобы играть в отношении ConcurrentDictionary и как сохранить значения потокобезопасными:
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);
}