在添加密钥之前检查字典中是否存在密钥的最佳方法?

Jam*_* Ko 10 c# performance dictionary hashtable data-structures

从词典中获取一个你不确定存在的键时,你通常会使用TryGetValue而不是ContainsKey+ get索引器来避免两次检查键的开销.换句话说,这个:

string password;
if (accounts.TryGetValue(username, out password))
{
    // use the password
}
Run Code Online (Sandbox Code Playgroud)

会更喜欢这个:

if (accounts.ContainsKey(username))
{
    string password = accounts[username];
}
Run Code Online (Sandbox Code Playgroud)

如果我想在将其设置为值之前检查密钥是否已存在,该怎么办?例如,我想在用新密码覆盖之前检查用户名是否存在:

if (!accounts.ContainsKey(username))
{
    accounts.Add(username, password);
}
else
{
    Console.WriteLine("Username is taken!");
}
Run Code Online (Sandbox Code Playgroud)

VS

// this doesn't exist
if (!accounts.TrySetValue(username, password))
{
    Console.WriteLine("Username is taken!");
}
Run Code Online (Sandbox Code Playgroud)

是否有更高性能的替代方案,ContainsKey并且Add这样做?

Sco*_*ain 6

如果您认为插入新名称是常见情况,而尝试插入重复名称是罕见情况,您可能只想利用捕获异常的开销。

try
{
    accounts.Add(username, password);
}
catch (ArgumentException)
{
    Console.WriteLine("Username is taken!");
}
Run Code Online (Sandbox Code Playgroud)

如果您Add使用现有密钥调用ArgumentException,则会抛出 a 。即使您经常有重复项,这仍然可能比您的ContainsKey检查性能更高。

  • 此[答案](/sf/answers/923643311/) 中的数字表明,如果“罕见情况”发生的概率超过 0.16%(每 625 例中就有 1 例),则速度会较慢当异常增多时,try-catch & 会变得更糟,而 ContainsKey 检查会给 **大多数** 代码增加可忽略不计的开销。另外...如果有一种简单的方法可以避免常规程序流程中的异常,那么它始终是首选实践。请参阅阅读材料:https://msdn.microsoft.com/en-us/library/ms229009(v=vs.100).aspx、/sf/answers/62386131/、https://stackoverflow。 com/a/161965/804797 (2认同)

Bac*_*cks 5

如果您不想覆盖,我认为最好编写自己的扩展方法TryGetValue.没有标准方法.

要么

使用CuncurrentDictionary,它具有TryAdd方法,但你必须在同步开销.

所以,简单的答案 - 不,没有这样的方法.

  • @JamesKo是什么原因?你真的有性能问题吗?或者只是为了证明这是可能的?我认为,Dictionary <,>是非常非常优化的类.所以,如果你有性能问题 - 添加图形或其他东西,看看问题在这里,检查.而简短的回答 - 不,你不能:) (2认同)

Eni*_*ity 5

我倾向于根据需要编写自己的扩展。

例如,GetValueOrDefault像这样:

public static V GetValueOrDefault<K, V>(this IDictionary<K, V> @this, K key, Func<V> @default)
{
    return @this.ContainsKey(key) ? @this[key] : @default();
}
Run Code Online (Sandbox Code Playgroud)

它可以这样使用:

var password = accounts.GetValueOrDefault(username, () => null);
if (password != null)
{
    //do stuff
}
Run Code Online (Sandbox Code Playgroud)

或者SetValueIfExists

public static V SetValueIfExists<K, V>(this IDictionary<K, V> @this, K key, V value)
{
    if (@this.ContainsKey(key))
    {
        @this[key] = value;
    }
}
Run Code Online (Sandbox Code Playgroud)

或者SetValueIfNotExists

public static V SetValueIfNotExists<K, V>(this IDictionary<K, V> @this, K key, V value)
{
    if (!@this.ContainsKey(key))
    {
        @this[key] = value;
    }
}
Run Code Online (Sandbox Code Playgroud)