web-dev-qa-db-fra.com

Quand utiliser une SortedList <TKey, TValue> sur un SortedDictionary <TKey, TValue>?

Cela peut sembler être une copie de cette question , qui demande "Quelle est la différence entre SortedList et SortedDictionary ?" Malheureusement, les réponses ne font que citer la documentation MSDN (qui indique clairement qu'il existe des différences de performances et d'utilisation de la mémoire entre les deux), mais ne répondent pas réellement à la question.

En fait (et donc cette question ne donne pas les mêmes réponses), selon MSDN:

Le SortedList<TKey, TValue> générique class est un arbre de recherche binaire avec O (log n) récupération, où n est le nombre d'éléments dans le dictionnaire . En cela, il est similaire au SortedDictionary<TKey, TValue> générique classe. Les deux classes ont les mêmes modèles d'objet, et les deux ont O (log n) récupération. Où les deux classes diffère par la mémoire utilisée et la vitesse de insertion et retrait:

  • SortedList<TKey, TValue> utilise moins mémoire que SortedDictionary<TKey, TValue>.

  • SortedDictionary<TKey, TValue> a insertion et retrait plus rapides opérations pour données non triées, O (log n) par opposition à O(n) pour SortedList<TKey, TValue>.

  • Si la liste est remplie en une fois À partir des données triées, SortedList<TKey, TValue> est plus rapide que SortedDictionary<TKey, TValue>.

Ainsi, cela indiquerait clairement que SortedList<TKey, TValue> est le meilleur choix sauf si vous avez besoin d'insérer et de supprimer des opérations plus rapidement pour les données unsorted.

La question demeure, compte tenu des informations ci-dessus, quelles sont les raisons pratiques (réalité, analyse de rentabilisation, etc.) de l'utilisation d'un SortedDictionary<TKey, TValue>? D'après les informations sur les performances, cela impliquerait qu'il n'y a vraiment pas besoin d'avoir SortedDictionary<TKey, TValue> du tout.

60
Scott Dorman

Je ne suis pas sûr de la précision de la documentation MSDN sur SortedList et SortedDictionary. Il semble être en train de dire que les deux sont implémentés en utilisant un arbre de recherche binaire. Mais si SortedList utilise un arbre de recherche binaire, pourquoi serait-il beaucoup plus lent pour les ajouts que SortedDictionary?

Quoi qu'il en soit, voici quelques résultats de tests de performance. 

Chaque test fonctionne sur une SortedList/SortedDictionary contenant 10 000 clés int32. Chaque test est répété 1 000 fois (version finale, démarrage sans débogage). 

Le premier groupe de tests ajoute les clés dans l'ordre, de 0 à 9 999. Le deuxième groupe de tests ajoute des clés aléatoires mélangées entre 0 et 9 999 (chaque nombre est ajouté exactement une fois).

***** Tests.PerformanceTests.SortedTest

SortedDictionary Add sorted: 4411 ms
SortedDictionary Get sorted: 2374 ms


SortedList Add sorted: 1422 ms
SortedList Get sorted: 1843 ms

***** Tests.PerformanceTests.UnsortedTest

SortedDictionary Add unsorted: 4640 ms
SortedDictionary Get unsorted: 2903 ms


SortedList Add unsorted: 36559 ms
SortedList Get unsorted: 2243 ms

Comme pour tout profilage, l’important est la performance relative et non les chiffres réels. 

Comme vous pouvez le constater, sur les données triées, la liste triée est plus rapide que SortedDictionary. Sur les données non triées, la SortedList est légèrement plus rapide lors de l'extraction, mais environ 9 fois plus lente lors de l'ajout.

Si les deux utilisent des arbres binaires en interne, il est assez surprenant que l'opération Add sur des données non triées soit beaucoup plus lente pour SortedList. Il est possible que la liste triée ajoute également des éléments à une structure de données linéaire triée en même temps, ce qui la ralentirait. 

Cependant, vous vous attendriez à ce que l'utilisation de la mémoire d'une SortedList soit égale ou supérieure ou au moins égale à une SortedDictionary. Mais cela contredit ce que dit la documentation MSDN.

52
Ash

Je ne sais pas pourquoi MSDN dit que SortedList<TKey, TValue> utilise un arbre binaire pour son implémentation, car si vous examinez le code avec un décompilateur comme Reflector, vous réalisez que ce n'est pas vrai.

SortedList<TKey, TValue> est simplement un tableau qui s'agrandit avec le temps.

Chaque fois que vous insérez un élément, il vérifie d'abord si le tableau a une capacité suffisante. Sinon, un tableau plus grand est recréé et les anciens éléments y sont copiés (comme List<T>).

Après cela, il cherche where pour insérer l'élément, en utilisant une recherche binaire (cela est possible car le tableau est indexable et déjà trié).

Pour garder le tableau trié, il déplace (ou pousse) tous les éléments situés après la position de l'élément à insérer d'une position (à l'aide de Array.Copy()).

Par exemple : 

// we want to insert "3" 

2  
4  <= 3
5
8
9
.      
.      
.  

// we have to move some elements first

2
.  <= 3
4 
5  |
8  v
9
.
.

Cela explique pourquoi les performances de SortedList sont si mauvaises lorsque vous insérez des éléments non triés. Il doit recopier certains éléments presque chaque insertion. Le seul cas où cela ne doit pas être fait est lorsque l'élément doit être inséré à la fin du tableau.

SortedDictionary<TKey, TValue> est différent et utilise un arbre binaire pour insérer et récupérer des éléments. Cela a aussi un coût lors de l'insertion car parfois l'arbre doit être rééquilibré (mais pas à chaque insertion).

Les performances sont assez similaires lors de la recherche d'un élément avec SortedList ou SortedDictionary car ils utilisent tous les deux une recherche binaire. 


À mon avis, vous devriez jamais utiliser SortedList pour trier simplement un tableau. Sauf si vous avez très peu d'éléments, il sera toujours plus rapide d'insérer des valeurs dans une liste (ou un tableau), puis d'appeler la méthode Sort().

SortedList est surtout utile lorsque vous avez une liste de valeurs déjà triée (par exemple, de la base de données), vous souhaitez la conserver et effectuer certaines opérations qui en tireraient profit (par exemple: la méthode Contains() de SortedList effectue une recherche binaire recherche linéaire)

SortedDictionary offre les mêmes avantages que SortedList mais fonctionne mieux si les valeurs à insérer ne sont pas déjà triées.


EDIT: Si vous utilisez .NET Framework 4.5, une alternative à SortedDictionary<TKey, TValue> est SortedSet<T>. Cela fonctionne de la même manière que SortedDictionary, en utilisant un arbre binaire, mais les clés et les valeurs sont les mêmes ici.

33
tigrou

Sont-ils destinés à deux fins différentes?

Il n’ya pas beaucoup de différence sémantique entre ces deux types de collection dans .NET. Ils offrent tous les deux une recherche par clé et conservent les entrées dans l'ordre de tri des clés. Dans la plupart des cas, vous serez d'accord avec l'un ou l'autre. Le seul différenciateur serait peut-être le permis de récupération indexé SortedList.

Mais la performance?

Cependant, il existe une différence de performance qui pourrait constituer un facteur plus important pour choisir entre eux. Voici une vue tabulaire de leur complexité asymptotique.

+------------------+---------+----------+--------+----------+----------+---------+
| Collection       | Indexed | Keyed    | Value  | Addition |  Removal | Memory  |
|                  | lookup  | lookup   | lookup |          |          |         |
+------------------+---------+----------+--------+----------+----------+---------+
| SortedList       | O(1)    | O(log n) | O(n)   | O(n)*    | O(n)     | Lesser  |
| SortedDictionary | n/a     | O(log n) | O(n)   | O(log n) | O(log n) | Greater |
+------------------+---------+----------+--------+----------+----------+---------+

* Insertion is O(1) for data that are already in sort order, so that each 
  element is added to the end of the list (assuming no resize is required).

Résumé

En résumé, vous voulez un SortedList<K, V> quand:

  1. vous avez besoin d'une recherche indexée.
  2. il est souhaitable d'avoir moins de surcharge de mémoire.
  3. vos données d'entrée sont déjà triées (supposons que vous les ayez déjà commandées auprès de la base de données).

Vous préféreriez préférer un SortedDictionary<K, V> lorsque:

  1. relative globale les performances sont importantes (en ce qui concerne la mise à l'échelle).
  2. vos données d'entrée ne sont pas ordonnées.

Code d'écriture

SortedList<K, V> et SortedDictionary<K, V> implémentent tous deux IDictionary<K, V>. Ainsi, dans votre code, vous pouvez renvoyer IDictionary<K, V> depuis la méthode ou déclarer une variable sous la forme IDictionary<K, V>. Masquer les détails de l'implémentation et coder par rapport à l'interface. 

IDictionary<K, V> x = new SortedDictionary<K, V>(); //for eg. 

À l'avenir, il est plus facile de choisir l'une ou l'autre solution si vous n'êtes pas satisfait des caractéristiques de performance d'une collection.


Pour plus d'informations sur les deux types de collection, voir l'original question lié. 

10
nawfal

Représentation visuelle des différences de performance.

enter image description here

3
Lev

C'est tout ce qu'on peut en dire. La récupération des clés est comparable, mais l’ajout est beaucoup plus rapide avec les dictionnaires.

J'essaie d'utiliser SortedList autant que possible car cela me permet de parcourir les clés et les collections de valeurs. Pour autant que je sache, ce n'est pas possible avec SortedDictionary.

Je ne suis pas sûr de cela, mais pour autant que je sache, les dictionnaires stockent des données dans des arborescences, alors que List stocke des données dans des tableaux linéaires. Cela explique pourquoi l'insertion et la suppression sont beaucoup plus rapides avec les dictionnaires, car moins de mémoire doit être déplacée. Cela explique également pourquoi vous pouvez effectuer une itération sur SortedLists mais pas sur SortedDictionary.

2
David Rutten

Une considération importante pour nous est le fait que nous avons souvent de petits dictionnaires (<100 éléments) et que les processeurs actuels accèdent beaucoup plus rapidement à la mémoire séquentielle tout en effectuant peu de branches difficiles à prévoir. (c'est-à-dire parcourir un tableau linéaire plutôt que de parcourir un arbre) Ainsi, lorsque votre dictionnaire contient moins de 60 éléments, SortedList <> est souvent le dictionnaire le plus rapide et le plus efficace en termes de mémoire dans de nombreux cas d'utilisation.

0
user3290232