我必须在C#中哈希两次吗?

Joe*_*e H 5 c#

我有以下代码:

class MyClass
{
    string Name;
    int NewInfo;
}

List<MyClass> newInfo = .... // initialize list with some values
Dictionary<string, int> myDict = .... // initialize dictionary with some values

foreach(var item in newInfo)
{
    if(myDict.ContainsKey(item.Name)) // 'A' I hash the first time here
         myDict[item.Name] += item.NewInfo // 'B' I hash the second (and third?) time here
    else
        myDict.Add(item.Name, item.NewInfo);
}
Run Code Online (Sandbox Code Playgroud)

有没有办法避免在字典中进行两次查找 - 第一次查看是否包含条目,第二次更新值?在'B'行上甚至可能有两个哈希查找 - 一个用于获取int值,另一个用于更新它.

Jon*_*eet 14

是的 - 使用Dictionary.TryGetValue.它接受一个out参数来接收值,并返回是否找到该值.这是您调整后的代码:

foreach(var item in newInfo)
{
    int value;
    if (myDict.TryGetValue(item.Name, out value))
    {
        myDict[item.Name] = value + item.NewInfo;
    }
    else
    {
        myDict[item.Name] = item.NewInfo;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,在这种特殊情况下,我们可以做得更好.如果键- 没有找到,out因为我们要设定新的价值无论是参数设置为0 item.NewInfoitem.NewInfo + value我们真的做着同样的事情正在无论哪种方式.我们可以忽略方法的返回值,只需使用:

foreach(var item in newInfo)
{
    int value;
    myDict.TryGetValue(item.Name, out value);
    myDict[item.Name] = value + item.NewInfo;
}
Run Code Online (Sandbox Code Playgroud)

这很不寻常 - 通常你使用返回值.

插话

这只是因为你真的做了一个有效的GetValueOrDefault操作.实际上,这将是一对有效的扩展方法:

public static TValue GetValueOrDefault<TKey, TValue>
    (this IDictionary<TKey, TValue> dictionary, TKey key)
{
    TValue value;
    dictionary.TryGetValue(key, out value);
    return value;
}

public static TValue GetValueOrDefault<TKey, TValue>
    (this IDictionary<TKey, TValue> dictionary, TKey key,
     TValue customDefault)
{
    TValue value;
    if (dictionary.TryGetValue(key, out value))
    {
        return value;
    }
    else
    {
        return customDefault;
    }
}
Run Code Online (Sandbox Code Playgroud)

在这一点上,你可以让你的代码清晰简洁:

foreach(var item in newInfo)
{
    myDict[item.Name] = myDict.GetValueOrDefault(item.Name) + item.NewInfo;
}
Run Code Online (Sandbox Code Playgroud)

(你可以要求GetValueOrDefault(item.Name, 0)更清晰.)

回到这一点......

请注意,您仍在进行两次查找 - 一次用于获取值,另一次用于添加/替换它.如果不使TValue类型参数变得可变,你就无法避免这种情况,你可以在适当的位置进行更改.那是可能的,但不是非常好.

在原始代码中,您可能会进行三次查找 - 一次查找ContainsKey,然后两次(如果找到密钥)则替换该值.如果我们扩展以下内容,则更容易看到+=:

myDict[item.Name] = myDict[item.Name] + item.NewInfo;
Run Code Online (Sandbox Code Playgroud)

(item.Name只会被评估一次,但除此之外它是相同的.)

另一个题外话

最好有一个Dictionary基于函数进行"查找和替换"的操作,以获得基于旧值的新值,例如

bool Update(TKey key, Func<TValue, bool, TValue> replacementFunction)
Run Code Online (Sandbox Code Playgroud)

其中replacementFunction将采取的电流值(或默认值的函数TValue,如果键没有被发现)和布尔说的键是否真正发现,并返回新值.然后字典可以查找密钥,调用替换函数并更新值.(这个不能作为扩展方法实现.)