2010-04-23 19 views
3

Ma question est est-il sûr pour l'énumérateur de supprimer l'élément de SortedList?Est-il sécuritaire de supprimer de SortedList au cours de l'itération

SortedList<decimal, string> myDictionary; 
// omitted code 

IEnumerator<decimal, string> enum = myDictionary.GetEnumerator(); 

while(enum.MoveNext) 
{ 
    // is it ok to remove here? 
    myDictionary.Remove(enum.Current.Key); 
} 
+3

Non, vous devriez obtenir un InvalidOperationExcpetion – bitbonk

+1

Vous devez également renommer votre variable 'enum' être' @ enum' comme 'enum' est un mot réservé . – James

+2

Oui, il est parfaitement sûr, car il ne fonctionnera pas :) S'il vous plaît envisager d'interroger votre collection pour acquérir des éléments à supprimer (par exemple LINQ) –

Répondre

8

Ce lèveront une exception - vous ne pouvez pas modifier une collection tout en réitérant dessus.

Si vous y réfléchissez un peu, vous comprendrez pourquoi. Si l'ajout ou la suppression de la collection était autorisé, vous ne répéteriez plus la même collection - vous en aurez trop (en ajoutant) ou pas assez (en supprimant).

+0

Points bonus si vous pouvez expliquer pourquoi cela ne devrait pas être autorisé. –

+0

Voilà: "La séquence d'index est basée sur la séquence de tri.Lorsqu'un élément est ajouté, il est inséré dans SortedList dans l'ordre de tri correct et l'indexation est ajustée en conséquence. Lorsqu'un élément est supprimé, l'indexation s'ajuste également en conséquence. Par conséquent, l'index d'une paire clé/valeur spécifique peut changer lorsque des éléments sont ajoutés ou supprimés de l'objet SortedList. "MSDN, documentation de la liste liée –

+1

Je pensais aux itérateurs STL où certains d'entre eux pouvaient être supprimés en toute sécurité et d'autres pas –

2

Les opérations de la liste pendant les itérations ne sont généralement pas prises en charge. Le comportement attendu est de lancer une exception, mais même si une collection ne parvient pas à le faire, vous ne devez pas vous en remettre à cela correctement.

Vous pouvez d'abord copier les éléments dans une autre liste, puis parcourir cette nouvelle liste d'éléments à modifier.

2

Non. InvalidOperationExcpetion est levé. Je suis d'accord que les éléments déjà énumérés peuvent être supprimés puisqu'il existe un index fixe. Cependant le problème est le suivant:

L'implémentation de SortedList n'est pas assez intelligente pour comprendre que la suppression n'aura aucun effet sur l'exécution ultérieure de l'énumérable. Et pour le garder simple et performant, il ne devrait pas.

4

Comme déjà dit, ce que vous cherchez à faire n'est pas possible. Cependant, une solution alternative consisterait simplement à maintenir une liste d'éléments marqués pour suppression, puis à supprimer ces mots postérieurs. J'opterais aussi pour un foreach plutôt qu'une boucle while, moins de code par ex.

var removeList = new List<decimal>(); 
foreach (var item in myDictionary) 
{ 
    // have a condition which indicates which items are to be removed 
    if (item.Key > 1) 
    { 
     removeList.Add(item.Key); 
    } 
} 

Ou si vous essayez simplement de récupérer des éléments à supprimer, utilisez LINQ

var removeList = myDictionary.Where(pair => pair.Key > 1).Select(k => k.Key).ToList(); 

Ensuite, il suffit de les retirer de la liste.

// remove from the main collection 
foreach (var key in removeList) 
{ 
    myDictionary.Remove(key); 
} 
+2

Etes-vous sûr que votre deuxième exemple va fonctionner? Je ne l'ai pas essayé, mais votre 'removeList' est juste une requête et aussi itérer * à la volée * sur la liste, conduisant à la même exception qu'auparavant. Je pense que vous devez ajouter un '.ToList()' ou '.ToArray()' pour en obtenir un nouveau, donc – Oliver

+0

@Oliver: +1 bonne place, je ne me suis jamais testé moi-même Mise à jour – James

2

Comme d'autres l'ont déjà signalé, cela ne fonctionnera pas. Cependant, comme la collection est une SortedList, vous pouvez utiliser la méthode RemoveAt.

Cette méthode a un profil de mémoire légèrement meilleur car elle ne nécessite aucun surdébit contrairement à une augmentation de O (n) utilisant une liste séparée pour suivre les suppressions. Il aurait aussi un profil de performance O (n^2) par opposition à O (n^2 * log (n)). La méthode RemoveAt est O (n) car elle doit effectuer une copie de tableau. La méthode Remove ajoute une opération O (log (n)) pour trouver l'index avant d'appeler en interne RemoveAt. Tout cela ne vous concerne probablement pas, mais il est utile de savoir au cas où vous rencontrez des situations impliquant beaucoup de «n».

var myDictionary = new SortedList<decimal, string>(); 

// omitted code 

int i = 0; 
while (myDictionary.Count > 0 && i < myDictionary.Count) 
{ 
    if (/* predicate to use for removal */) 
    { 
    myDictionary.RemoveAt(i); 
    } 
    else 
    { 
    i++; 
    } 
} 
+0

Nice! Si vous voulez vérifier l'élément 'i' dans le' if' vous pouvez le faire en utilisant ' myDictionary.Values ​​[i] ', comme la liste sous-jacente est trié. – v01pe

0

Une autre solution:

  int counter= MyDictionary.Count; 
      if (counter == 0) 
       return; 

      for (int i = 0; i < counter;i++) 
      { 
       KeyValuePair<MyIdentifier, MyValue> key = (KeyValuePair<MyIdentifier, MyValue>)MyDictionary.ToArray()[i]; 
       MyIdentifier identifier = null; 

       if (key.Key != null) 
        identifier = key.Key as MyIdentifier; 

       if (identifier != null) 
        if (MyCondition) 
        { 
         MyDictionary.Remove(identifier); 
         counter--; 
        } 
      }