Reputation: 46651
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
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
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