CodeGym /Corsi /C# SELF /Ordinamento delle collezioni

Ordinamento delle collezioni

C# SELF
Livello 29 , Lezione 2
Disponibile

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?
List<T>
Sort()
Sì: lambda o IComparer
T[]
Array.Sort()
Sì: lambda o IComparer
Dictionary<TKey, TValue>
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();
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION