简体   繁体   English

如何增加 HashTable 中的值?

[英]How can I increase the value in a HashTable?

I have a HashTable which I am keep track of colors (which are the keys) and count of colors which is the key.我有一个哈希表,我正在跟踪 colors (这是键)和 colors 的计数,这是键。

I am trying to figure out how to increment the key when it the HashTable already contains the color.我试图弄清楚当 HashTable 已经包含颜色时如何增加键。 Here is a code snippet:这是一个代码片段:

Hashtable htColors = new Hashtable();

if (htColors.Contains(color))
{
    // Want to increase the "value" of the key here.       
}
else
{
    htColors.Add(color, 1); //Found color for first time
}

I'm posting this to be pedantic.我发布这个是迂腐的。 I don't like the interfacing to Dictionary because there is a cost to this very common kind of access - if your most common case is touching an element that already exists, you have to hash and look up your value 3 times.我不喜欢与 Dictionary 的接口,因为这种非常常见的访问方式是有成本的——如果你最常见的情况是触及一个已经存在的元素,你必须 hash 并查找你的值 3 次。 Don't believe me?不相信我? I wrote DK's solution here:我在这里写了 DK 的解决方案:

static void AddInc(Dictionary<string, int> dict, string s)
{
    if (dict.ContainsKey(s))
    {
        dict[s]++;
    }
    else
    {
        dict.Add(s, 1);
    }
}

When put into IL - you get this:当放入 IL - 你得到这个:

L_0000: nop 
L_0001: ldarg.0 
L_0002: ldarg.1 
L_0003: callvirt instance bool [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::ContainsKey(!0)
L_0008: ldc.i4.0 
L_0009: ceq 
L_000b: stloc.0 
L_000c: ldloc.0 
L_000d: brtrue.s L_0028
L_000f: nop 
L_0010: ldarg.0 
L_0011: dup 
L_0012: stloc.1 
L_0013: ldarg.1 
L_0014: dup 
L_0015: stloc.2 
L_0016: ldloc.1 
L_0017: ldloc.2 
L_0018: callvirt instance !1 [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::get_Item(!0)
L_001d: ldc.i4.1 
L_001e: add 
L_001f: callvirt instance void [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::set_Item(!0, !1)
L_0024: nop 
L_0025: nop 
L_0026: br.s L_0033
L_0028: nop 
L_0029: ldarg.0 
L_002a: ldarg.1 
L_002b: ldc.i4.1 
L_002c: callvirt instance void [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::Add(!0, !1)
L_0031: nop 
L_0032: nop 
L_0033: ret

which calls to ContainsKey, get_item, and set_item, all of which hash and look up.它调用 ContainsKey、get_item 和 set_item,所有这些 hash 并查找。

I wrote something less pretty which uses a class that holds an int and the class lets you side-effect it (you can't really use a struct without incurring the same penalty because of struct copying semantics).我写了一些不太漂亮的东西,它使用了一个包含一个 int 的 class 的 class 让你产生副作用(你不能真正使用一个结构而不会因为结构复制语义而受到同样的惩罚)。

class IntegerHolder {
    public IntegerHolder(int x) { i = x; }
    public int i;
}
static void AddInc2(Dictionary<string, IntegerHolder> dict, string s)
{
    IntegerHolder holder = dict[s];
    if (holder != null)
    {
        holder.i++;
    }
    else
    {
        dict.Add(s, new IntegerHolder(1));
    }
}

This gives you the following IL:这为您提供了以下 IL:

L_0000: nop 
L_0001: ldarg.0 
L_0002: ldarg.1 
L_0003: callvirt instance !1 [mscorlib]System.Collections.Generic.Dictionary`2<string, class AddableDictionary.IntegerHolder>::get_Item(!0)
L_0008: stloc.0 
L_0009: ldloc.0 
L_000a: ldnull 
L_000b: ceq 
L_000d: stloc.1 
L_000e: ldloc.1 
L_000f: brtrue.s L_0023
L_0011: nop 
L_0012: ldloc.0 
L_0013: dup 
L_0014: ldfld int32 AddableDictionary.IntegerHolder::i
L_0019: ldc.i4.1 
L_001a: add 
L_001b: stfld int32 AddableDictionary.IntegerHolder::i
L_0020: nop 
L_0021: br.s L_0033
L_0023: nop 
L_0024: ldarg.0 
L_0025: ldarg.1 
L_0026: ldc.i4.1 
L_0027: newobj instance void AddableDictionary.IntegerHolder::.ctor(int32)
L_002c: callvirt instance void [mscorlib]System.Collections.Generic.Dictionary`2<string, class AddableDictionary.IntegerHolder>::Add(!0, !1)
L_0031: nop 
L_0032: nop 
L_0033: ret 

Which calls get_item once - there is no additional hashing in the case of an object present.一次调用 get_item - 在存在 object 的情况下没有额外的散列。 I got a little sleazy and made the field public to avoid the method calls for property access.我有点邋遢,并公开了该字段,以避免对属性访问的方法调用。

If it were me, I would wrap this overall functionality into its own class and hide the IntegerHolder class from public view - here's a limited version:如果是我,我会将这个整体功能包装到它自己的 class 中,并从公众视野中隐藏 IntegerHolder class - 这是一个有限的版本:

public class CountableItem<T>
{
    private class IntegerHolder
    {
        public int i;
        public IntegerHolder() { i = 1; }
    }
    Dictionary<T, IntegerHolder> dict = new Dictionary<T, IntegerHolder>();

    public void Add(T key)
    {
        IntegerHolder val = dict[key];
        if (val != null)
            val.i++;
        else
            dict.Add(key, new IntegerHolder());
    }

    public void Clear()
    {
        dict.Clear();
    }

    public int Count(T key)
    {
        IntegerHolder val = dict[key];
        if (val != null)
            return val.i;
        return 0;
    }

    // TODO - write the IEnumerable accessor.
}

Try the following尝试以下

if (htColors.Contains(color))
{
   int old = (int)htColors[color];
   htColor[color] = old + 1;
}

EDIT Response to comments编辑对评论的回应

IMHO, the Dictionary approach is much better because it is 1) type safe and 2) eliminates the boxing involved in this solution.恕我直言,Dictionary 方法要好得多,因为它是 1) 类型安全和 2) 消除了此解决方案中涉及的装箱。

Having the line be the following won't affect the key, just the value如下所示不会影响键,只会影响值

htColor[color] = (int)htColor[color] + 1;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM