1. Introduction
Si t’as déjà eu un tiroir à chaussettes où tu galérais à trouver une paire, t’as déjà rencontré le problème du tri. Quand tout est en vrac, trouver ce que tu veux devient une vraie quête. En prog avec les collections, c’est pareil.
Le tri, c’est le process qui consiste à organiser les éléments d’une collection selon un critère (genre par ordre alphabétique, par valeur, par date, etc.). C’est important pour :
- L’affichage des données à l’utilisateur (personne n’aime le chaos).
- Faciliter la recherche (genre la recherche binaire, c’est possible que sur des collections triées).
- Comparaisons, rapports, export et d’autres opérations.
Question à un million : "Et si j’ai que cinq éléments, je peux ne pas trier ?" — Théoriquement, ouais, mais dès que cinq devient cinq cents ou cinq mille, sans automatisation, t’es mort.
En .NET, t’as deux approches principales pour trier :
- Modifier la collection d’origine (genre trier un List<T> avec la méthode Sort).
- Créer une nouvelle copie triée de la collection (genre cloner un tableau puis le trier).
Tri des listes avec la méthode .Sort()
La méthode Sort() existe sur la classe List<T>, vu que cette classe implémente l’interface IList<T>, ce qui permet d’accéder par index et de changer l’ordre des éléments.
Exemple — tri des nombres par ordre croissant :
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
var numbers = new List<int> { 5, 2, 9, 1, 5, 6 };
numbers.Sort();
Console.WriteLine("Tri par ordre croissant :");
foreach(var number in numbers)
{
Console.Write($"{number} "); // 1 2 5 5 6 9
}
}
}
Ici, le tri se fait "sur place" : la liste d’origine numbers est modifiée, les éléments sont réarrangés.
Tri des chaînes de caractères
Le tri marche aussi nickel pour les strings :
var words = new List<string> { "orange", "pomme", "banane", "poire" };
words.Sort();
Console.WriteLine(string.Join(", ", words)); // orange, banane, poire, pomme
Fun fact : par défaut, le tri des strings se fait selon l’ordre Unicode, pas selon l’alphabet "humain" (surtout pour les langues différentes, pense-y pour les applis multilingues).
2. Tri selon des règles custom
Parfois, le tri "par défaut" ne suffit pas. Genre, tu veux trier des users non pas par nom, mais par âge ou date d’inscription.
Tri avec une expression lambda (méthode Sort(Comparison<T>))
Tu peux appeler la méthode Sort en passant une règle de tri spéciale — une fonction qui compare deux éléments.
Tri des users par âge :
class User
{
public string Name { get; set; }
public int Age { get; set; }
}
// ...
var users = new List<User>
{
new User { Name = "Alice", Age = 30 },
new User { Name = "Bob", Age = 25 },
new User { Name = "Eva", Age = 35 }
};
users.Sort((u1, u2) => u1.Age.CompareTo(u2.Age));
foreach (var user in users)
{
Console.WriteLine($"{user.Name}: {user.Age}");
}
// Affichage :
// Bob: 25
// Alice: 30
// Eva: 35
Comment ça marche cette magie ? La lambda (u1, u2) => u1.Age.CompareTo(u2.Age) renvoie un nombre négatif si u1 est plus jeune, positif s’il est plus vieux, et 0 si les âges sont égaux.
Utiliser l’interface IComparer<T>
Parfois, tu veux sortir la règle de tri dans une classe à part, genre si t’as plein de collections et une logique de tri compliquée.
class UserAgeComparer : IComparer<User>
{
public int Compare(User x, User y)
{
return x.Age.CompareTo(y.Age);
}
}
// ...
var users = new List<User>{ /* ... */ };
users.Sort(new UserAgeComparer()); // maintenant tri par âge
C’est pratique si tu dois trier à plusieurs endroits du code ou si t’as plein de tris différents.
Tri sur plusieurs champs à la main
Si certains users ont le même âge, mais tu veux les trier par nom à l’intérieur du même groupe d’âge :
users.Sort((u1, u2) => {
int ageCompare = u1.Age.CompareTo(u2.Age);
if (ageCompare != 0)
return ageCompare;
else
return u1.Name.CompareTo(u2.Name);
});
Maintenant, le tri se fait d’abord par âge, puis par nom.
3. Tri d’une copie de la collection (sans toucher à l’originale)
Si tu veux pas modifier la liste d’origine, clone-la avant de trier :
var copy = new List<int>(numbers);
copy.Sort();
Pour les objets custom, pareil :
var usersCopy = new List<User>(users);
usersCopy.Sort((a, b) => a.Age.CompareTo(b.Age));
4. Tri des tableaux
Avec les tableaux (T[]), c’est aussi super simple :
int[] numbers = { 4, 2, 9, 7 };
Array.Sort(numbers); // modifie le tableau d’origine
Pour un tri "custom" :
Array.Sort(numbers, (a, b) => b.CompareTo(a)); // tri décroissant
Fais gaffe, Array.Sort modifie le tableau d’origine, il ne renvoie pas un nouveau. Si tu veux garder l’original, clone le tableau avant :
int[] oldNumbers = { 3, 2, 1 };
int[] copy = (int[])oldNumbers.Clone();
Array.Sort(copy);
5. "Tri" des dictionnaires (Dictionary<TKey, TValue>)
Dictionary<TKey, TValue> — c’est une collection non ordonnée par nature (donc pas de garantie d’ordre quand tu parcours). Mais parfois, tu veux des "paires triées".
Pour avoir des clés ou valeurs triées, crée une liste, copie les paires dedans, et trie comme tu veux :
var dict = new Dictionary<string, int>
{
{ "pomme", 2 },
{ "orange", 5 },
{ "poire", 3 }
};
// Tri par clé :
var keyValueList = new List<KeyValuePair<string, int>>(dict);
keyValueList.Sort((a, b) => a.Key.CompareTo(b.Key));
foreach (var kv in keyValueList)
{
Console.WriteLine($"{kv.Key}: {kv.Value}");
}
// Tri par valeur :
keyValueList.Sort((a, b) => a.Value.CompareTo(b.Value));
foreach (var kv in keyValueList)
{
Console.WriteLine($"{kv.Key}: {kv.Value}");
}
Si tu veux juste une liste triée des clés ou des valeurs :
var sortedKeys = new List<string>(dict.Keys);
sortedKeys.Sort();
var sortedValues = new List<int>(dict.Values);
sortedValues.Sort();
Mais retiens que la structure Dictionary ne change pas — tu obtiens juste une énumération triée. Dans .NET 9, il y aura OrderedDictionary<TKey, TValue>, qui garde l’ordre des éléments. Pas souvent utile, mais parfois ça sert.
6. Astuces utiles
Faut-il absolument implémenter une interface de comparaison ?
C’est simple : si tu veux que tes objets "sachent" comment se comparer (genre par date ou nom), implémente l’interface IComparable<T>.
class Product : IComparable<Product>
{
public string Name { get; set; }
public decimal Price { get; set; }
public int CompareTo(Product other)
{
return Price.CompareTo(other.Price);
}
}
Maintenant tu peux faire :
var products = new List<Product> { /* ... */ };
products.Sort(); // trie par prix
Si le tri par défaut ne te va pas — utilise IComparer<T> ou passe une lambda comme plus haut.
Tri d’une liste d’utilisateurs par ordre alphabétique
Imaginons qu’on a une liste d’utilisateurs dans l’appli :
List<string> users = new List<string> { "Victor", "Anna", "Catherine", "Boris" };
users.Sort(); // maintenant users est trié : Anna, Boris, Victor, Catherine
Répartition des tâches par priorité
class Task
{
public string Title { get; set; }
public int Priority { get; set; } // 1 - urgent, 2 - important, 3 - peut attendre
}
var todo = new List<Task>
{
new Task { Title = "Faire les devoirs", Priority = 2 },
new Task { Title = "Acheter du pain", Priority = 1 },
new Task { Title = "Regarder une série", Priority = 3 }
};
todo.Sort((a, b) => a.Priority.CompareTo(b.Priority));
foreach (var task in todo)
Console.WriteLine($"{task.Priority}: {task.Title}");
Comparaison des méthodes de tri
| Collection | Modifiée sur place ? | Méthode de tri | Règle custom possible ? |
|---|---|---|---|
|
Oui | |
Oui : lambda ou IComparer |
|
Oui | |
Oui : lambda ou IComparer |
|
Non | — (on crée une liste et on trie) | Oui : via lambda ou IComparer |
7. Erreurs classiques et subtilités du tri
Tri sur place — ça modifie la collection d’origine ! Si tu veux pas toucher aux données de base, clone-les avant.
Essayer de trier une collection immuable (genre ReadOnlyCollection<T>) va te donner une erreur à l’exécution.
Comparaison des strings différente selon la culture : le tri des strings (surtout avec de la cyrillique, des trémas, etc.) peut changer selon la locale. Pour un tri correct, utilise Comparer.Create(...) avec la culture voulue.
Le tri des dictionnaires ne change pas leur structure — tu obtiens toujours une nouvelle séquence de paires (ou une nouvelle liste).
Pour un ordre compliqué, utilise l’interface IComparer<T> avec ta logique.
Voici un exemple de tri foireux (à éviter dans la vraie vie) :
var numbers = new List<int> { 1, 2, 3 };
var sorted = numbers.Sort(); // ERREUR : Sort() renvoie void !
La bonne façon :
numbers.Sort(); // modifie numbers sur place
// Si tu veux une nouvelle collection :
var sorted = new List<int>(numbers);
sorted.Sort();
GO TO FULL VERSION