Xaisoft
Xaisoft

Reputation: 46651

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.

I am trying to figure out how to increment the key when it the HashTable already contains the color. 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
}

Upvotes: 2

Views: 9348

Answers (2)

plinth
plinth

Reputation: 49209

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. Don't believe me? I wrote DK's solution here:

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:

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.

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).

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:

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. 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:

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.
}

Upvotes: 5

JaredPar
JaredPar

Reputation: 755467

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.

Having the line be the following won't affect the key, just the value

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

Upvotes: 4

Related Questions