2009-03-06 7 views
2

J'ai un HashTable dont je garde la trace des couleurs (qui sont les clés) et le nombre de couleurs qui est la clé.Comment puis-je augmenter la valeur dans un HashTable?

J'essaie de comprendre comment incrémenter la clé quand la table de hachage contient déjà la couleur. Voici un extrait de code:

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 
} 

Répondre

4

Effectuez les opérations suivantes

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

EDIT Réponse aux commentaires

à mon humble avis, l'approche de dictionnaire est beaucoup mieux car il est 1) de type sûr et 2) élimine la boxe impliquée dans cette solution.

ayant la ligne la suivante n'affectera pas la clé, juste la valeur

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

Ok, votre solution semble fonctionner. Que penses-tu de l'alternative du dictionnaire? Aussi, si je faisais juste (int) htColors [couleur] + 1, cela changerait-il la clé ou la valeur? – Xaisoft

+0

Merci pour l'aide avec le HashTable. Il semble que je vais aller avec un dictionnaire après tout. – Xaisoft

+0

Quelqu'un peut me corriger si je me trompe ici, mais je crois que boxing/unboxing automatique se produit pour tout type de valeur qui est déplacé vers le tas, même (dans ce cas) ints dans un dictionnaire typé (Dictionary ). Quel Dictionnaire élimine est l'encombrement de la coulée. –

4

Je poste ce pour être pédant. Je n'aime pas l'interfaçage avec le dictionnaire car il y a un coût pour ce type d'accès très courant - si votre cas le plus courant touche un élément qui existe déjà, vous devez hacher et rechercher votre valeur 3 fois. Ne me crois pas? Je ici écrit la solution DK:

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

Lorsque mis en IL - vous obtenez ceci:

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 

qui appelle à ContainsKey, get_item et set_item, dont tous hachage et rechercher. J'ai écrit quelque chose de moins joli qui utilise une classe qui contient un int et la classe vous permet de l'affecter latéralement (vous ne pouvez pas vraiment utiliser une structure sans encourir la même pénalité à cause de la sémantique de copie de structure).

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)); 
    } 
} 

Cela vous donne l'IL suivante:

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 

qui appelle get_item une fois - il n'y a pas supplémentaire dans le hachage cas d'un objet présent. J'ai été un peu louche et j'ai rendu le champ public pour éviter les appels de méthode pour l'accès à la propriété.

Si c'était moi, je conclurai cette fonctionnalité globale dans sa propre classe et cacher la classe IntegerHolder de la vue du public - voici une version limitée:

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

+1 pour un très bon aperçu d'IL.Cela démontre pourquoi/quand utiliser un type de valeur pour le dictionnaire seau peut être une mauvaise idée. –

Questions connexes