elementat - c# dictionary initialization
Incrementar un valor numérico en un diccionario (6)
Aquí hay un buen método de extensión:
public static void Increment<T>(this Dictionary<T, int> dictionary, T key)
{
int count;
dictionary.TryGetValue(key, out count);
dictionary[key] = count + 1;
}
Uso:
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"]);
Estoy usando el código a continuación para incrementar o insertar un valor en un diccionario. Si la clave que estoy incrementando no existe, me gustaría establecer su valor en 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;
}
}
¿Es esta una forma apropiada de hacerlo?
Aquí hay una práctica prueba de unidad para que usted juegue con ConcurrentDictionary y cómo mantener los valores en hilo:
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);
}
Es legible y la intención es clara. creo que esto esta bien. No es necesario inventar un código más inteligente o más corto; si no mantiene la intención tan clara como tu versión inicial :-)
Dicho esto, aquí hay una versión un poco más corta:
public void IncrementCount(Dictionary<int, int> someDictionary, int id)
{
if (!someDictionary.ContainsKey(id))
someDictionary[id] = 0;
someDictionary[id]++;
}
Si tiene acceso simultáneo al diccionario, recuerde sincronizar el acceso a él.
Resultó que tenía sentido usar el ConcurrentDictionary que tiene el práctico método de inserción: AddOrUpdate.
Entonces, acabo de usar:
someDictionary.AddOrUpdate(id, 1, (id, count) => count + 1);
Solo algunas medidas en .NET 4 para llaves enteras.
No es exactamente una respuesta a su pregunta, pero en aras de la exhaustividad he medido el comportamiento de varias clases útiles para incrementar enteros basados en claves enteras: Array
simple, Dictionary
(enfoque de SortedDictionary
), Dictionary
(enfoque simple), SortedDictionary
(Enfoque de @Ani) y ConcurrentDictionary.TryAddOrUpdate
.
Aquí están los resultados, ajustados por 2.5 ns para envolver con clases en lugar de uso directo:
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
Y ese es el código .
Tenga en cuenta que ConcurrentDictionary.TryAddOrUpdate
es tres veces más lento que el setter del indexador TryGetValue
+ de Dictionary
. Y el último es diez veces más lento que Array.
Entonces usaría una matriz si sé que el rango de teclas es pequeño y de otra manera un enfoque combinado.
Tu código está bien. Pero aquí hay una manera de simplificar de una manera que no requiere ramificación en su código:
int currentCount;
// currentCount will be zero if the key id doesn''t exist..
someDictionary.TryGetValue(id, out currentCount);
someDictionary[id] = currentCount + 1;
Esto se basa en el hecho de que el método TryGetValue
establece el value
al valor predeterminado de su tipo si la clave no existe. En su caso, el valor predeterminado de int
es 0
, que es exactamente lo que quiere.
UPD . A partir de C # 7.0, este fragmento se puede acortar con out variables
:
// declare variable right where it''s passed
someDictionary.TryGetValue(id, out var currentCount);
someDictionary[id] = currentCount + 1;