Introduction aux collections
Les collections sont des structures de données qui permettent de stocker et manipuler des groupes d'objets de manière dynamique, contrairement aux tableaux qui ont une taille fixe.
List<T> - La liste générique
La liste est la collection la plus utilisée. Elle peut grandir et rétrécir dynamiquement.
using System;
using System.Collections.Generic;
List<string> prenoms = new List<string>();
prenoms.Add("Alice");
prenoms.Add("Bob");
prenoms.Add("Charlie");
List<int> nombres = new List<int> { 1, 2, 3, 4, 5 };
Console.WriteLine(prenoms[0]);
Console.WriteLine($"Nombre d'éléments: {prenoms.Count}");
Opérations courantes sur les listes
List<string> fruits = new List<string>();
fruits.Add("Pomme");
fruits.Add("Banane");
fruits.AddRange(new[] { "Orange", "Kiwi" });
fruits.Insert(1, "Fraise");
bool contientPomme = fruits.Contains("Pomme");
int indexOrange = fruits.IndexOf("Orange");
fruits.Remove("Banane");
fruits.RemoveAt(0);
fruits.Clear();
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}
Dictionary<TKey, TValue> - Le dictionnaire
Un dictionnaire stocke des paires clé-valeur, permettant un accès rapide aux données via une clé unique.
Dictionary<string, int> ages = new Dictionary<string, int>();
ages.Add("Alice", 25);
ages.Add("Bob", 30);
ages["Charlie"] = 35;
Dictionary<string, string> capitales = new Dictionary<string, string>
{
["France"] = "Paris",
["Espagne"] = "Madrid",
["Italie"] = "Rome"
};
int ageAlice = ages["Alice"];
Console.WriteLine($"Alice a {ageAlice} ans");
Opérations sur les dictionnaires
Dictionary<string, double> notes = new Dictionary<string, double>();
if (!notes.ContainsKey("Marie"))
{
notes["Marie"] = 16.5;
}
if (notes.TryGetValue("Marie", out double noteMarie))
{
Console.WriteLine($"Note de Marie: {noteMarie}");
}
else
{
Console.WriteLine("Marie n'a pas de note");
}
foreach (KeyValuePair<string, double> kvp in notes)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value}/20");
}
foreach (var (nom, note) in notes)
{
Console.WriteLine($"{nom}: {note}/20");
}
notes.Remove("Marie");
foreach (string nom in notes.Keys)
{
Console.WriteLine($"Étudiant: {nom}");
}
foreach (double note in notes.Values)
{
Console.WriteLine($"Note: {note}");
}
HashSet<T> - Ensemble sans doublons
Un HashSet stocke des éléments uniques sans ordre particulier, idéal pour éviter les doublons.
HashSet<string> couleurs = new HashSet<string>();
couleurs.Add("Rouge");
couleurs.Add("Vert");
couleurs.Add("Bleu");
couleurs.Add("Rouge");
Console.WriteLine($"Nombre de couleurs uniques: {couleurs.Count}");
HashSet<int> numerosUniques = new HashSet<int> { 1, 2, 3, 2, 1 };
if (couleurs.Contains("Rouge"))
{
Console.WriteLine("Rouge est présent");
}
HashSet<int> ensemble1 = new HashSet<int> { 1, 2, 3 };
HashSet<int> ensemble2 = new HashSet<int> { 3, 4, 5 };
ensemble1.UnionWith(ensemble2);
HashSet<int> intersection = new HashSet<int>(ensemble1);
intersection.IntersectWith(ensemble2);
Queue<T> et Stack<T>
Collections spécialisées pour gérer des données selon des règles spécifiques.
Queue<T> - File (FIFO)
Queue<string> fileAttente = new Queue<string>();
fileAttente.Enqueue("Alice");
fileAttente.Enqueue("Bob");
fileAttente.Enqueue("Charlie");
while (fileAttente.Count > 0)
{
string suivant = fileAttente.Dequeue();
Console.WriteLine($"Traitement de: {suivant}");
}
if (fileAttente.Count > 0)
{
string prochain = fileAttente.Peek();
}
Stack<T> - Pile (LIFO)
Stack<string> historique = new Stack<string>();
historique.Push("Page1");
historique.Push("Page2");
historique.Push("Page3");
while (historique.Count > 0)
{
string page = historique.Pop();
Console.WriteLine($"Retour à: {page}");
}
if (historique.Count > 0)
{
string actuelle = historique.Peek();
}
Choisir la bonne collection :
- List<T> : Collection générale, accès par index, ordre préservé
- Dictionary<K,V> : Accès rapide par clé unique
- HashSet<T> : Éléments uniques, pas d'ordre, tests de membership rapides
- Queue<T> : Premier entré, premier sorti (FIFO)
- Stack<T> : Dernier entré, premier sorti (LIFO)
LINQ avec les collections
Language Integrated Query (LINQ) permet de manipuler les collections facilement.
using System.Linq;
List<int> nombres = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> pairs = nombres.Where(n => n % 2 == 0).ToList();
List<int> carres = nombres.Select(n => n * n).ToList();
int somme = nombres.Sum();
double moyenne = nombres.Average();
int maximum = nombres.Max();
int minimum = nombres.Min();
int premier = nombres.First(n => n > 5);
bool existeGrand = nombres.Any(n => n > 100);
bool tousPositifs = nombres.All(n => n > 0);
List<int> tries = nombres.OrderByDescending(n => n).ToList();
Exercice pratique
Exercice :
Créez un système de gestion d'étudiants avec les fonctionnalités suivantes :
- Une classe Étudiant avec nom, âge, et liste de notes
- Un dictionnaire pour stocker les étudiants par numéro
- Des méthodes pour ajouter, rechercher, et calculer des statistiques
- Utilisation de LINQ pour les requêtes
Système de gestion d'étudiants complet
using System;
using System.Collections.Generic;
using System.Linq;
namespace GestionEtudiants
{
public class Etudiant
{
public int NumeroEtudiant { get; set; }
public string Nom { get; set; }
public string Prenom { get; set; }
public int Age { get; set; }
public List<double> Notes { get; set; }
public Etudiant(int numero, string nom, string prenom, int age)
{
NumeroEtudiant = numero;
Nom = nom;
Prenom = prenom;
Age = age;
Notes = new List<double>();
}
public double CalculerMoyenne()
{
return Notes.Count > 0 ? Notes.Average() : 0;
}
public void AjouterNote(double note)
{
if (note >= 0 && note <= 20)
{
Notes.Add(note);
Console.WriteLine($"Note {note} ajoutée pour {Prenom} {Nom}");
}
else
{
Console.WriteLine("Note invalide (doit être entre 0 et 20)");
}
}
public override string ToString()
{
string notesStr = Notes.Count > 0 ? string.Join(", ", Notes) : "Aucune note";
double moyenne = CalculerMoyenne();
return $"#{NumeroEtudiant} - {Prenom} {Nom} ({Age} ans)\n" +
$"Notes: [{notesStr}]\n" +
$"Moyenne: {moyenne:F2}/20";
}
}
public class GestionnaireEtudiants
{
private Dictionary<int, Etudiant> etudiants;
private int prochainNumero;
public GestionnaireEtudiants()
{
etudiants = new Dictionary<int, Etudiant>();
prochainNumero = 1000;
}
public int AjouterEtudiant(string nom, string prenom, int age)
{
Etudiant nouvelEtudiant = new Etudiant(prochainNumero, nom, prenom, age);
etudiants[prochainNumero] = nouvelEtudiant;
Console.WriteLine($"✅ Étudiant ajouté: {prenom} {nom} (#{prochainNumero})");
return prochainNumero++;
}
public Etudiant RechercherEtudiant(int numero)
{
return etudiants.TryGetValue(numero, out Etudiant etudiant) ? etudiant : null;
}
public List<Etudiant> RechercherParNom(string nom)
{
return etudiants.Values
.Where(e => e.Nom.ToLower().Contains(nom.ToLower()) ||
e.Prenom.ToLower().Contains(nom.ToLower()))
.ToList();
}
public void AjouterNote(int numeroEtudiant, double note)
{
Etudiant etudiant = RechercherEtudiant(numeroEtudiant);
if (etudiant != null)
{
etudiant.AjouterNote(note);
}
else
{
Console.WriteLine($"❌ Étudiant #{numeroEtudiant} non trouvé");
}
}
public void AfficherStatistiques()
{
if (!etudiants.Any())
{
Console.WriteLine("Aucun étudiant enregistré");
return;
}
var etudiantsAvecNotes = etudiants.Values.Where(e => e.Notes.Any()).ToList();
Console.WriteLine("\n📊 === STATISTIQUES ===");
Console.WriteLine($"Nombre total d'étudiants: {etudiants.Count}");
Console.WriteLine($"Étudiants avec des notes: {etudiantsAvecNotes.Count}");
if (etudiantsAvecNotes.Any())
{
double moyenneGenerale = etudiantsAvecNotes.Average(e => e.CalculerMoyenne());
var meilleurEtudiant = etudiantsAvecNotes.OrderByDescending(e => e.CalculerMoyenne()).First();
Console.WriteLine($"Moyenne générale: {moyenneGenerale:F2}/20");
Console.WriteLine($"Meilleur étudiant: {meilleurEtudiant.Prenom} {meilleurEtudiant.Nom} ({meilleurEtudiant.CalculerMoyenne():F2}/20)");
var repartition = etudiantsAvecNotes
.GroupBy(e => e.CalculerMoyenne() switch
{
>= 16 => "Très bien (16-20)",
>= 14 => "Bien (14-16)",
>= 12 => "Assez bien (12-14)",
>= 10 => "Passable (10-12)",
_ => "Insuffisant (0-10)"
})
.OrderByDescending(g => g.Key);
Console.WriteLine("\nRépartition par mention:");
foreach (var groupe in repartition)
{
Console.WriteLine($" {groupe.Key}: {groupe.Count()} étudiant(s)");
}
}
Console.WriteLine("========================\n");
}
public void AfficherTousLesEtudiants()
{
if (!etudiants.Any())
{
Console.WriteLine("Aucun étudiant enregistré");
return;
}
Console.WriteLine("\n👥 === LISTE DES ÉTUDIANTS ===");
foreach (var etudiant in etudiants.Values.OrderBy(e => e.NumeroEtudiant))
{
Console.WriteLine(etudiant);
Console.WriteLine("───────────────────────────────");
}
}
public List<Etudiant> ObtenirEtudiantsParMoyenne(double seuilMinimum)
{
return etudiants.Values
.Where(e => e.Notes.Any() && e.CalculerMoyenne() >= seuilMinimum)
.OrderByDescending(e => e.CalculerMoyenne())
.ToList();
}
}
class Program
{
static void Main(string[] args)
{
GestionnaireEtudiants gestionnaire = new GestionnaireEtudiants();
Console.WriteLine("🎓 === SYSTÈME DE GESTION D'ÉTUDIANTS ===");
int num1 = gestionnaire.AjouterEtudiant("Dupont", "Alice", 20);
int num2 = gestionnaire.AjouterEtudiant("Martin", "Bob", 22);
int num3 = gestionnaire.AjouterEtudiant("Durand", "Claire", 19);
Console.WriteLine("\n📝 Ajout de notes...");
gestionnaire.AjouterNote(num1, 16.5);
gestionnaire.AjouterNote(num1, 14.0);
gestionnaire.AjouterNote(num1, 18.5);
gestionnaire.AjouterNote(num2, 12.0);
gestionnaire.AjouterNote(num2, 15.5);
gestionnaire.AjouterNote(num3, 17.0);
gestionnaire.AjouterNote(num3, 19.0);
gestionnaire.AjouterNote(num3, 16.0);
gestionnaire.AfficherTousLesEtudiants();
gestionnaire.AfficherStatistiques();
Console.WriteLine("🔍 Recherche d'étudiants contenant 'du':");
var resultats = gestionnaire.RechercherParNom("du");
foreach (var etudiant in resultats)
{
Console.WriteLine($"- {etudiant.Prenom} {etudiant.Nom}");
}
Console.WriteLine("\n🏆 Étudiants avec une moyenne >= 15:");
var bonnesNotes = gestionnaire.ObtenirEtudiantsParMoyenne(15.0);
foreach (var etudiant in bonnesNotes)
{
Console.WriteLine($"- {etudiant.Prenom} {etudiant.Nom}: {etudiant.CalculerMoyenne():F2}/20");
}
Console.WriteLine("\nAppuyez sur une touche pour quitter...");
Console.ReadKey();
}
}
}
Points clés à retenir
- List<T> : Collection dynamique la plus polyvalente
- Dictionary<K,V> : Accès rapide par clé, parfait pour les associations
- HashSet<T> : Éléments uniques, opérations ensemblistes
- Queue<T> / Stack<T> : Collections spécialisées FIFO/LIFO
- LINQ : Requêtes puissantes sur les collections
- Performance : Choisir la bonne collection selon l'usage