Collections en C# : Liste, Dictionnaire, File, Pile
Découvrez les collections en C# (List, Dictionary, Queue, Stack) pour gérer des données efficacement avec exemples.
En C#, les collections permettent de stocker plusieurs valeurs de manière dynamique et de les traiter efficacement.
Les tableaux ont une taille fixe, mais les collections peuvent grandir ou rétrécir selon les besoins.
Cet article présente List<T>, Dictionary<TKey,TValue> (et KeyValuePair<,>),
SortedList<TKey,TValue>, Queue<T>, Stack<T>,
HashSet<T> et LinkedList<T> avec des exemples.
List<T>
List<T> est une liste dynamique qui stocke des éléments du même type dans l’ordre.
Elle offre de nombreuses méthodes pour ajouter/supprimer, rechercher et trier.
using System;
using System.Collections.Generic;
using System.Linq;
var nombres = new List<int> { 5, 1, 9 };
nombres.Add(3); // ajouter à la fin
nombres.Insert(1, 7); // insérer à un index donné
nombres.Remove(9); // supprimer par valeur (première occurrence)
nombres.RemoveAll(x => x % 2 == 1); // supprimer tous les nombres impairs
nombres.AddRange(new[] { 10, 2, 8 });
nombres.Sort(); // trier croissant
nombres.Reverse(); // inverser l’ordre
Console.WriteLine(string.Join(", ", nombres));
Console.WriteLine(nombres.Contains(8)); // true
// Filtrage LINQ
var pairs = nombres.Where(n => n % 2 == 0).ToList();
Console.WriteLine(string.Join(", ", pairs));
Dictionary<TKey,TValue> et KeyValuePair<TKey,TValue>
Dictionary stocke des paires clé–valeur. Les clés sont uniques.
Basé sur un hash, il offre des recherches rapides.
using System;
using System.Collections.Generic;
var prix = new Dictionary<string, decimal>(StringComparer.OrdinalIgnoreCase)
{
["Pomme"] = 5.75m,
["Poire"] = 7.25m
};
// Ajouter ou mettre à jour
prix["Banane"] = 12m; // ajoute si non présent, met à jour sinon
// Lecture sécurisée
if (prix.TryGetValue("POMME", out var valeur))
Console.WriteLine($"Pomme: {valeur} EUR");
// Vérifier l’existence d’une clé
if (!prix.ContainsKey("Cerise"))
prix.Add("Cerise", 18.5m);
// Itération avec KeyValuePair
foreach (KeyValuePair<string, decimal> kv in prix)
Console.WriteLine($"{kv.Key} → {kv.Value} EUR");
// Mettre à jour une valeur
prix["Pomme"] = prix["Pomme"] * 1.10m; // +10% prix
Astuce : KeyValuePair<TKey,TValue> fournit .Key et .Value
lors de l’itération d’un dictionnaire. Pour la mise à jour, utilisez directement dict[key].
SortedList<TKey,TValue>
SortedList conserve les clés dans l’ordre croissant et permet un accès indexé via Keys et Values.
C’est un mélange entre dictionnaire et tableau.
using System;
using System.Collections.Generic;
var listePrix = new SortedList<string, decimal>(StringComparer.OrdinalIgnoreCase)
{
["Poire"] = 7.25m,
["Pomme"] = 5.75m,
["Banane"] = 12m,
["Cerise"] = 18.5m
};
// Clés triées :
Console.WriteLine(string.Join(", ", listePrix.Keys)); // Banane, Cerise, Poire, Pomme (ordre croissant)
// Accès par index :
for (int i = 0; i < listePrix.Count; i++)
Console.WriteLine($"#{i} {listePrix.Keys[i]} → {listePrix.Values[i]}");
// Comparateur personnalisé (ordre décroissant) :
var decroissant = new SortedList<string, decimal>(Comparer<string>.Create((a,b) => StringComparer.OrdinalIgnoreCase.Compare(b,a)));
decroissant["Pomme"] = 5.75m;
decroissant["Poire"] = 7.25m;
Console.WriteLine(string.Join(", ", decroissant.Keys)); // ordre inversé
SortedListdéplace des éléments lors des insertions/suppressions pour conserver l’ordre. Si beaucoup d’opérations, utiliser plutôtSortedDictionary.- Pour accéder à des plages par index,
SortedListest avantageuse.
Queue<T> (FIFO)
Queue fonctionne selon le principe First In, First Out ; utile pour les files d’attente et flux de travail.
using System;
using System.Collections.Generic;
var commandes = new Queue<string>();
commandes.Enqueue("Commande#1001");
commandes.Enqueue("Commande#1002");
commandes.Enqueue("Commande#1003");
Console.WriteLine(commandes.Peek()); // prochain sans retirer : Commande#1001
Console.WriteLine(commandes.Dequeue()); // retire : Commande#1001
Console.WriteLine(commandes.Count); // 2
Stack<T> (LIFO)
Stack fonctionne selon le principe Last In, First Out ; utilisé pour “annuler” (undo), historique, etc.
using System;
using System.Collections.Generic;
var etapes = new Stack<string>();
etapes.Push("Étape-1");
etapes.Push("Étape-2");
etapes.Push("Étape-3");
Console.WriteLine(etapes.Peek()); // Étape-3
Console.WriteLine(etapes.Pop()); // Étape-3 (retiré)
Console.WriteLine(etapes.Pop()); // Étape-2
HashSet<T>
HashSet conserve un ensemble d’éléments uniques. Les doublons sont ignorés.
Idéal pour les opérations ensemblistes (union, intersection, différence).
using System;
using System.Collections.Generic;
var a = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "pomme", "poire", "banane" };
var b = new HashSet<string> { "Banane", "cerise" };
var union = new HashSet<string>(a);
union.UnionWith(b); // A ∪ B
var intersection = new HashSet<string>(a);
intersection.IntersectWith(b); // A ∩ B
var difference = new HashSet<string>(a);
difference.ExceptWith(b); // A \ B
Console.WriteLine(string.Join(", ", union)); // pomme, poire, banane, cerise
Console.WriteLine(string.Join(", ", intersection)); // banane
Console.WriteLine(string.Join(", ", difference)); // pomme, poire
Console.WriteLine(a.Add("Pomme")); // false (déjà présent, insensible à la casse)
Astuce : Les tests d’appartenance (Contains) et ajouts sont généralement en O(1) amorti dans HashSet ; idéal pour filtrer les doublons dans de grands ensembles de données.
LinkedList<T>
LinkedList est une liste doublement chaînée. Insertion/suppression sur des nœuds connus : O(1).
Pas d’accès aléatoire par index ; il faut parcourir.
using System;
using System.Collections.Generic;
var liste = new LinkedList<string>();
var n1 = liste.AddFirst("Début");
var n2 = liste.AddLast("Fin");
liste.AddAfter(n1, "Milieu-1"); // Début <-> Milieu-1 <-> Fin
liste.AddBefore(n2, "Milieu-2"); // Début <-> Milieu-1 <-> Milieu-2 <-> Fin
// Supprimer un nœud
var milieu1 = n1.Next;
liste.Remove(milieu1);
// Parcours
for (var n = liste.First; n != null; n = n.Next)
Console.WriteLine(n.Value); // Début, Milieu-2, Fin
- Rapide pour les opérations en tête/en fin et insertion/suppression par rapport à un nœud connu.
- Pas adapté à l’accès aléatoire par index ; utiliser
List<T>dans ce cas.
Exemple d’application : Pipeline de traitement des commandes
Dans ce scénario, différentes collections sont combinées : Queue pour les commandes en attente, Dictionary pour le stock et les prix, HashSet pour les clients uniques, Stack pour annuler (undo), SortedList pour la liste des prix et LinkedList pour les « produits récemment consultés ».
using System;
using System.Collections.Generic;
var enAttente = new Queue<string>(new[] { "CMD-1001", "CMD-1002" });
var stock = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
{
["Pomme"] = 100, ["Poire"] = 50, ["Banane"] = 30
};
var prix = new SortedList<string, decimal> { ["Poire"] = 7.25m, ["Pomme"] = 5.75m, ["Banane"] = 12m };
var clients = new HashSet<string>(); // identifiants clients uniques
var undo = new Stack<Action>(); // pile d’annulation
var recents = new LinkedList<string>(); // produits récemment consultés
void AjouterAuPanier(string produit, int qte)
{
if (stock.TryGetValue(produit, out var s) && s >= qte)
{
stock[produit] = s - qte;
undo.Push(() => stock[produit] += qte); // action d’annulation
recents.AddFirst(produit);
Console.WriteLine($"{qte} x {produit} ajouté. Restant : {stock[produit]}");
}
else
{
Console.WriteLine($"Stock insuffisant : {produit}");
}
}
void TraiterCommande(string noCommande, string clientId)
{
Console.WriteLine($"Traitement : {noCommande}");
clients.Add(clientId);
AjouterAuPanier("Pomme", 3);
AjouterAuPanier("Poire", 2);
}
while (enAttente.Count > 0)
{
var cmd = enAttente.Dequeue();
TraiterCommande(cmd, "CLI-001");
}
Console.WriteLine($"Clients uniques : {clients.Count}");
Console.WriteLine($"Liste de prix (triée) : {string.Join(", ", prix.Keys)}");
// Annuler si nécessaire :
if (undo.Count > 0)
{
var retour = undo.Pop();
retour();
}
Console.WriteLine("Produits récemment consultés (Top 3) :");
int k = 0;
for (var n = recents.First; n != null && k < 3; n = n.Next, k++)
Console.WriteLine($"- {n.Value}");
Résumé (TL;DR)
- List<T> : Liste dynamique, basée sur index. Idéale pour l’accès aléatoire.
- Dictionary<TKey,TValue> : Paires clé–valeur, recherches rapides. Itérer avec KeyValuePair.
- SortedList<TKey,TValue> : Clés triées + accès par index. Utile pour des plages de valeurs.
- Queue<T> (FIFO) : Workflows/files d’attente.
- Stack<T> (LIFO) : Annulation, historique.
- HashSet<T> : Éléments uniques ; opérations ensemblistes (∪, ∩, \).
- LinkedList<T> : Liste chaînée ; insertion/suppression rapide en tête/fin ou autour d’un nœud connu.
Guide : Accès aléatoire → List, recherche clé rapide → Dictionary, clés triées + index → SortedList, unicité → HashSet, FIFO → Queue, LIFO → Stack, opérations rapides tête/fin → LinkedList.
Articles connexes
Boucles en C# (for, foreach, while, do-while)
Apprenez à utiliser les boucles for, foreach, while et do-while en C# pour gérer des actions répétitives avec des exemples pratiques.
Expressions Lambda en C#
Apprenez les expressions lambda en C#, avec une syntaxe concise, Func et Action, et des exemples pratiques avec LINQ.
Génériques en C# (List<T>, Dictionary<TKey,TValue>)
Apprenez les génériques en C# (List<T>, Dictionary<TKey,TValue>) pour écrire un code réutilisable et typé, avec exemples.
Tableaux (Arrays) en C#
Apprenez les tableaux en C# : déclaration, indexation, parcours avec boucles et opérations courantes avec exemples.