Initialisation des systèmes...

Baptiste.Dev
Retour aux notes
LangagesIntermédiaireSérie : C# / .NET

C# : Collections & génériques

Le point d'entrée sur System.Collections.Generic : pourquoi les génériques, les deux familles (classes et interfaces) et comment choisir la bonne structure.

Par Baptiste Vidal
2 min de lecture
Mis à jour hier
c#csharpcollectionsgenericssystem.collections.generic

C# : Collections & génériques

System.Collections.Generic est le namespace qui regroupe les collections typées de C# : List<T>, Dictionary<TKey, TValue>, HashSet<T>, Queue<T>, Stack<T> et leurs interfaces.

Cette page est la vue d'ensemble : pourquoi ces collections sont génériques, les deux familles à connaître, et comment choisir la bonne. Chaque branche a ensuite sa page détaillée.


# Pourquoi "génériques" ?

Le <T> dans List<T>, c'est un paramètre de type : tu fixes une fois le type des éléments, et le compilateur garantit le reste. Avant les génériques (C# 1.0), on utilisait ArrayList et Hashtable, qui stockent des object. Trois problèmes en découlent :

// Ancien monde (à éviter) : System.Collections
ArrayList liste = new ArrayList();
liste.Add(1);
liste.Add("oups");            // aucun contrôle : on peut tout mélanger
 
int x = (int)liste[0];        // cast obligatoire au retour
int y = (int)liste[1];        // PLANTE à l'exécution : InvalidCastException
  • Pas de sûreté de type : on peut y mettre n'importe quoi, les erreurs explosent à l'exécution.
  • Cast permanent : chaque lecture demande un (int).
  • Boxing : chaque type valeur (int, bool...) est emballé dans un object sur le tas, ce qui coûte cher.
// Monde moderne : System.Collections.Generic
List<int> liste = new List<int>();
liste.Add(1);
// liste.Add("oups");         // ERREUR de compilation, détectée tout de suite
 
int x = liste[0];             // pas de cast, pas de boxing

Règle simple : utilise toujours les collections génériques. ArrayList et Hashtable ne servent plus que pour du très vieux code.


# Deux familles à connaître

L'espace de noms se lit en deux couches complémentaires :

Les interfaces (les contrats)          Les classes (les implémentations)
IEnumerable<T>                         List<T>, T[]
ICollection<T>                         Dictionary<TKey, TValue>
IList<T> / ISet<T> / IDictionary       HashSet<T>, Queue<T>, Stack<T>...
  • Les classes concrètes sont ce que tu instancies au quotidien (new List<int>()). Elles décident du comportement et des performances.
  • Les interfaces sont les contrats que ces classes implémentent. Programmer contre elles (accepter un IEnumerable<T> plutôt qu'une List<T>) rend ton code flexible et testable.

On développe chacune de son côté :


# Choisir la bonne collection

Le réflexe le plus utile : partir de ce que tu vas faire des données.

CollectionAccèsRechercheAjoutOrdreQuand l'utiliser
List<T>index O(1)O(n)fin O(1)insertionle choix par défaut, séquence indexée
Dictionary<K,V>clé O(1)O(1)O(1)aucunassocier une clé à une valeur
HashSet<T>-O(1)O(1)aucununicité, appartenance rapide
Queue<T>--O(1)FIFOfile d'attente, BFS
Stack<T>--O(1)LIFOpile, undo, DFS
LinkedList<T>O(n)O(n)nœud O(1)insertioninsertions au milieu très fréquentes
SortedDictionary<K,V>clé O(log n)O(log n)O(log n)trié par cléclé vers valeur, toujours ordonné

Dans le doute, commence par List<T>. Passe à Dictionary dès que tu fais des recherches par clé répétées, et à HashSet dès que tu testes souvent l'appartenance.


# Par où continuer