1. Introduzione
Se hai mai avuto un cassetto pieno di calzini e hai provato a trovare una coppia, hai già incontrato il problema dell’ordinamento. Quando è tutto un casino, trovare quello che ti serve diventa una quest. Con le collezioni in programmazione è la stessa storia.
L’ordinamento è il processo di mettere in ordine gli elementi di una collezione secondo un certo criterio (tipo per ordine alfabetico, per valore, per data ecc). È importante per:
- Mostrare i dati all’utente (nessuno ama il caos).
- Semplificare la ricerca (ad esempio, la ricerca binaria funziona solo su collezioni ordinate).
- Confronti, report, export e altre operazioni.
Domanda da un milione: "E se ho solo cinque elementi, posso non ordinare?" — Teoricamente sì, ma appena cinque diventano cinquecento o cinquemila, senza automazione non ce la fai.
In .NET ci sono due approcci principali per ordinare:
- Modificare la collezione originale (tipo ordinare List<T> col metodo Sort).
- Creare una nuova copia ordinata della collezione (tipo clonare un array e poi ordinarlo).
Ordinare le liste con il metodo .Sort()
Il metodo Sort() ce l’ha la classe List<T>, perché questa classe implementa l’interfaccia IList<T>, che ti dà accesso per indice e la possibilità di cambiare l’ordine degli elementi.
Esempio — ordinare numeri in ordine crescente:
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("Ordinamento crescente:");
foreach(var number in numbers)
{
Console.Write($"{number} "); // 1 2 5 5 6 9
}
}
}
Qui l’ordinamento avviene "sul posto": la lista originale numbers viene modificata, gli elementi si spostano.
Ordinare le stringhe
L’ordinamento funziona alla grande anche per le stringhe:
var words = new List<string> { "arancia", "mela", "banana", "pera" };
words.Sort();
Console.WriteLine(string.Join(", ", words)); // arancia, banana, mela, pera
Curiosità: di default l’ordinamento delle stringhe segue l’ordine Unicode, non quello "umano" dell’alfabeto (soprattutto con lingue diverse, occhio se fai app multilingua).
2. Ordinamento con regole custom
A volte l’ordinamento "di default" non basta. Tipo, vuoi ordinare gli utenti non per nome, ma per età o data di registrazione.
Ordinamento con lambda expression (metodo Sort(Comparison<T>))
Il metodo Sort lo puoi chiamare passando una regola custom — una funzione che confronta due elementi.
Ordinare utenti per età:
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}");
}
// Output:
// Bob: 25
// Alice: 30
// Eva: 35
Come funziona questa magia? La lambda (u1, u2) => u1.Age.CompareTo(u2.Age) restituisce un numero negativo se u1 è più giovane, positivo se è più vecchio, e 0 se hanno la stessa età.
Usare l’interfaccia IComparer<T>
A volte vuoi mettere la regola di ordinamento in una classe a parte, tipo se hai tante collezioni e logica complicata.
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()); // ora ordina per età
Comodo se ti serve lo stesso ordinamento in più punti del programma o hai tante regole diverse.
Ordinare su più campi a mano
Se alcuni utenti hanno la stessa età, ma vuoi ordinarli per nome dentro ogni gruppo di età:
users.Sort((u1, u2) => {
int ageCompare = u1.Age.CompareTo(u2.Age);
if (ageCompare != 0)
return ageCompare;
else
return u1.Name.CompareTo(u2.Name);
});
Ora l’ordinamento è prima per età, poi per nome.
3. Ordinare una copia della collezione (senza toccare l’originale)
Se non vuoi cambiare la lista originale, copiala prima di ordinare:
var copy = new List<int>(numbers);
copy.Sort();
Per oggetti custom — stessa cosa:
var usersCopy = new List<User>(users);
usersCopy.Sort((a, b) => a.Age.CompareTo(b.Age));
4. Ordinare gli array
Con gli array (T[]) è pure facile:
int[] numbers = { 4, 2, 9, 7 };
Array.Sort(numbers); // modifica l’array originale
Per ordinamento custom:
Array.Sort(numbers, (a, b) => b.CompareTo(a)); // ordinamento decrescente
Occhio che Array.Sort modifica l’array originale, non ne restituisce uno nuovo. Se vuoi tenere l’originale, copialo prima:
int[] oldNumbers = { 3, 2, 1 };
int[] copy = (int[])oldNumbers.Clone();
Array.Sort(copy);
5. "Ordinare" i dizionari (Dictionary<TKey, TValue>)
Dictionary<TKey, TValue> — è una collezione non ordinata per natura (cioè non garantisce l’ordine quando la scorri). Ma a volte vuoi ottenere "coppie ordinate".
Per avere chiavi o valori ordinati, crea una lista, copia lì le coppie e ordina come vuoi:
var dict = new Dictionary<string, int>
{
{ "mela", 2 },
{ "arancia", 5 },
{ "pera", 3 }
};
// Ordinamento per chiave:
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}");
}
// Ordinamento per valore:
keyValueList.Sort((a, b) => a.Value.CompareTo(b.Value));
foreach (var kv in keyValueList)
{
Console.WriteLine($"{kv.Key}: {kv.Value}");
}
Se vuoi solo la lista ordinata delle chiavi o dei valori:
var sortedKeys = new List<string>(dict.Keys);
sortedKeys.Sort();
var sortedValues = new List<int>(dict.Values);
sortedValues.Sort();
Ma ricorda, la struttura Dictionary non cambia — ottieni solo una sequenza ordinata. In .NET 9 arriverà OrderedDictionary<TKey, TValue>, che mantiene l’ordine degli elementi. Non serve spesso, ma a volte è utile.
6. Sfumature utili
Devi per forza implementare l’interfaccia di confronto?
Semplice: se vuoi che i tuoi oggetti "sappiano" come confrontarsi (tipo per data o nome), implementa l’interfaccia 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);
}
}
Ora puoi fare:
var products = new List<Product> { /* ... */ };
products.Sort(); // ordina per prezzo
Se il confronto di default non ti va bene — usa IComparer<T> o passa una lambda, come visto sopra.
Ordinare una lista di utenti in ordine alfabetico
Mettiamo che nell’app hai una lista di utenti:
List<string> users = new List<string> { "Vittorio", "Anna", "Caterina", "Boris" };
users.Sort(); // ora users è ordinato: Anna, Boris, Caterina, Vittorio
Distribuire i task per urgenza
class Task
{
public string Title { get; set; }
public int Priority { get; set; } // 1 - urgente, 2 - importante, 3 - si può rimandare
}
var todo = new List<Task>
{
new Task { Title = "Fare i compiti", Priority = 2 },
new Task { Title = "Comprare il pane", Priority = 1 },
new Task { Title = "Guardare una serie", Priority = 3 }
};
todo.Sort((a, b) => a.Priority.CompareTo(b.Priority));
foreach (var task in todo)
Console.WriteLine($"{task.Priority}: {task.Title}");
Confronto tra i modi di ordinare
| Collezione | Si modifica sul posto? | Metodo per ordinare | Si può dare una regola? |
|---|---|---|---|
|
Sì | |
Sì: lambda o IComparer |
|
Sì | |
Sì: lambda o IComparer |
|
No | — (crei una lista e la ordini) | Sì: con lambda o IComparer |
7. Errori tipici e sfumature dell’ordinamento
Ordinamento sul posto — modifica la collezione originale! Se i dati originali non devono cambiare — copiali prima.
Provare a ordinare una collezione non modificabile (tipo ReadOnlyCollection<T>) ti darà un errore a runtime.
Confronto delle stringhe diverso tra culture: l’ordinamento delle stringhe (soprattutto con cirillico, umlaut ecc.) può cambiare tra le varie localizzazioni. Per ordinare bene usa Comparer.Create(...) con la cultura giusta.
Ordinare i dizionari non cambia la loro struttura — ottieni sempre una nuova sequenza di coppie (o una nuova lista).
Per ordinamenti complessi usa l’interfaccia IComparer<T> con la tua logica.
Ecco un esempio di ordinamento sbagliato (da evitare nella pratica):
var numbers = new List<int> { 1, 2, 3 };
var sorted = numbers.Sort(); // ERRORE: Sort() restituisce void!
Corretto:
numbers.Sort(); // modifica numbers sul posto
// Se vuoi una nuova collezione:
var sorted = new List<int>(numbers);
sorted.Sort();
GO TO FULL VERSION