方法在字典中添加新项或更新现有项

在一些遗留代码中,我看到了以下扩展方法,以方便添加一个新的键-值项或更新值,如果键已经存在。

方法-1(遗留代码)。

public static void CreateNewOrUpdateExisting<TKey, TValue>(
this IDictionary<TKey, TValue> map, TKey key, TValue value)
{
if (map.ContainsKey(key))
{
map[key] = value;
}
else
{
map.Add(key, value);
}
}

不过,我已经检查过map[key]=value执行完全相同的工作。也就是说,这个方法可以替换为下面的method -2。

方法2。

public static void CreateNewOrUpdateExisting<TKey, TValue>(
this IDictionary<TKey, TValue> map, TKey key, TValue value)
{
map[key] = value;
}

现在,我的问题是…如果我把Method-1换成Method-2会有问题吗?它会在任何可能的情况下断裂吗?

另外,我认为这曾经是哈希表和字典之间的区别。HashTable允许更新项目,或使用索引器添加新项目,而Dictionary不允许!!这个差异在c# > 3.0版本中被消除了吗?

该方法的目的是如果用户再次发送相同的键值,该方法只需用新值更新条目,如果新的键值对已经发送给该方法,则创建一个新的条目。

228996 次浏览

没有问题。我甚至会从源代码中删除CreateNewOrUpdateExisting,并直接在代码中使用map[key] = value,因为这是惯用的c#;c#开发人员通常知道map[key] = value表示添加或更新。

如果我把Method-1换成Method-2会有问题吗?

不,只使用map[key] = value。这两个选项是等价的。


关于Dictionary<>Hashtable:当你启动Reflector时,你会看到这两个类的索引设置器调用this.Insert(key, value, add: false);,而add形参负责在插入重复键时抛出异常。所以这两个类的行为是一样的。

在功能上,它们是等价的。

在性能方面map[key] = value会更快,因为你只进行一次查找而不是两次。

风格方面,越短越好:)

在大多数情况下,代码在多线程上下文中工作得很好。然而,它是线程安全的,没有额外的同步。

老问题了,但我觉得我应该添加以下内容,甚至更多,因为在写这个问题的时候。net 4.0已经发布了。

从。net 4.0开始,有命名空间System.Collections.Concurrent,其中包括线程安全的集合。

集合System.Collections.Concurrent.ConcurrentDictionary<>做的正是你想要的。它具有AddOrUpdate()方法,具有线程安全的额外优势。

如果你在一个高性能的场景中,不处理多线程,已经给出的map[key] = value的答案会更快。

在大多数情况下,这种性能优势是微不足道的。如果是这样,我建议使用ConcurrentDictionary,因为:

  1. 它在框架中——它经过了更多的测试,而且你不是那个必须维护代码的人
  2. 它是可扩展的:如果你切换到多线程,你的代码已经准备好了

唯一的问题是,如果有一天

映射[key] = value

将转化为-

地图(例子)+ +;

这会导致KeyNotFoundException。

我知道它不是Dictionary<TKey, TValue>类,但是你可以在增加一个值时避免KeyNotFoundException,比如:

dictionary[key]++; // throws `KeyNotFoundException` if there is no such key

通过使用ConcurrentDictionary< TKey TValue>以及它的AddOrUpdate()方法。

让我举个例子:

var str = "Hellooo!!!";
var characters = new ConcurrentDictionary<char, int>();
foreach (var ch in str)
characters.AddOrUpdate(ch, 1, (k, v) => v + 1);

方法2更好:

  • 更少的代码
  • 完全相同(除了一些不可能的边缘情况)

下面是一个基准示例,用于演示方法2的性能优势。

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;


public class Program
{
public static void Main()
{
BenchmarkRunner.Run<DictionaryBenchmarks>();
}
}


[MemoryDiagnoser]
public class DictionaryBenchmarks
{
private readonly IDictionary<int, bool> _map = new Dictionary<int, bool>();
private readonly int[] _arr = new int[20]
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
};


[Benchmark]
public void CreateNewOrUpdateExistingWithContainsKeyCheck()
{
foreach (int item in _arr)
{
if (_map.ContainsKey(item))
{
_map[item] = true;
}
else
{
_map.Add(item, true);
}
}
}


[Benchmark]
public void CreateNewOrUpdateExistingWithoutContainsKeyCheck()
{
foreach (int item in _arr)
{
_map[item] = true;
}
}
}
< span style=" font - family:宋体;">是< / th > < span style=" font - family:宋体;"> < / th >错误 < span style=" font - family:宋体;"> StdDev < / th > < span style=" font - family:宋体;"> < / th >分配 . . < span style=" font - family:宋体;"> - < / td > . . < span style=" font - family:宋体;"> - < / td >
方法
CreateNewOrUpdateExistingWithContainsKeyCheck 232.0 ns 4.19 ns10.52 ns
CreateNewOrUpdateExistingWithoutContainsKeyCheck 131.9 ns4.83 ns