增加字典中的数值

Kin*_*tor 65 c# dictionary

我正在使用下面的代码来增加或插入字典中的值.如果我正在递增的键不存在,我想将其值设置为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;
     }
 }
Run Code Online (Sandbox Code Playgroud)

这是一种合适的方式吗?

Kin*_*tor 81

事实证明使用ConcurrentDictionary是有意义的,它具有方便的upsert方法:AddOrUpdate.

所以,我刚刚用过:

someDictionary.AddOrUpdate(id, 1, (id, count) => count + 1);  
Run Code Online (Sandbox Code Playgroud)

  • 这个答案是线程安全的,与其他几个不同. (5认同)
  • 这很好,但我想有一些处理线程问题的开销. (2认同)

Ani*_*Ani 65

你的代码很好.但这是一种简化方式,不需要在代码中进行分支:

int currentCount;

// currentCount will be zero if the key id doesn't exist..
someDictionary.TryGetValue(id, out currentCount); 

someDictionary[id] = currentCount + 1;
Run Code Online (Sandbox Code Playgroud)

这取决于如果密钥不存在,则该TryGetValue方法设置value为其类型的默认值.在你的情况下,默认值int0,这是你想要什么.


UPD.从C#7.0开始,可以使用out variables以下命令缩短此代码段:

// declare variable right where it's passed
someDictionary.TryGetValue(id, out var currentCount); 
someDictionary[id] = currentCount + 1;
Run Code Online (Sandbox Code Playgroud)

  • @JBSnorro:不,这样会好起来的; 我鼓励你试一试.请注意,正在调用索引器的*setter*,而不是getter.从[文档](http://msdn.microsoft.com/en-us/library/9tee9ht2.aspx):"当您设置属性值时,如果键位于Dictionary <TKey,TValue>中,则值与该键相关联的键将被指定的值替换.**如果该键不在Dictionary <TKey,TValue>中,则键和值将添加到字典中.**" (22认同)
  • 这里的所有示例都不是线程安全的.这不是问题的一部分,因此假设它是如此,这将是一个错误. (15认同)
  • 我意识到这个节目有点晚了,但我想我会帮助其他人.上述代码将在真正的多线程环境中失败.在TryGetValue和它下面的递增函数之间,Dictionary可以在另一个线程上更新,这意味着currentCount将不同步并且您创建了竞争条件. (8认同)

yel*_*ood 15

这是一个很好的扩展方法:

    public static void Increment<T>(this Dictionary<T, int> dictionary, T key)
    {
        int count;
        dictionary.TryGetValue(key, out count);
        dictionary[key] = count + 1;
    }
Run Code Online (Sandbox Code Playgroud)

用法:

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"]);
Run Code Online (Sandbox Code Playgroud)

  • 我意识到这个节目有点晚了,但我想我会帮助其他人.上述代码将在真正的多线程环境中失败.在TryGetValue和它下面的递增函数之间,可以在另一个线程上更新Dictionary,这意味着count将不同步并且您创建了竞争条件. (5认同)

dri*_*iis 14

它是可读的,意图很明确.我觉得这很好.无需发明更智能或更短的代码; 如果它没有像你的初始版本那样保持意图:-)

话虽这么说,这是一个稍短的版本:

public void IncrementCount(Dictionary<int, int> someDictionary, int id)
{
    if (!someDictionary.ContainsKey(id))
        someDictionary[id] = 0;

    someDictionary[id]++;
}
Run Code Online (Sandbox Code Playgroud)

如果您可以同时访问字典,请记住同步对它的访问.

  • @inflagranti - 这也是.(它在返回之前递增零 - 这是在方法中获得更少条件的一种方式的示例). (4认同)
  • 是的,当ID不存在时,它确实需要额外的查找.我把"最好的方式"看作"更短/更简单".如果"最佳方式"意味着"性能最佳",则原始版本应该稍微更高效.但是,我怀疑它是否可以测量,除非在紧密循环中使用它. (2认同)

tsu*_*sul 5

只需在.NET 4上对整数键进行一些测量.

这不是你的问题的答案,但为了完整起见,我已经测量了各种类的行为,这些类对于基于整数键递增整数很有用:简单Array,Dictionary(@ Ani的方法),Dictionary(简单方法),SortedDictionary(@ Ani的方法) )和ConcurrentDictionary.TryAddOrUpdate.

这是结果,调整2.5 ns用于包装类而不是直接使用:

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
Run Code Online (Sandbox Code Playgroud)

就是代码.

需要注意的是ConcurrentDictionary.TryAddOrUpdate比慢三倍DictionaryTryGetValue + indexer的setter.而后者比Array慢十倍.

如果我知道键的范围很小,那么我会使用数组,否则就会使用组合方法.