In Java, un Comparator è un’interfaccia funzionale usata per definire un criterio di ordinamento personalizzato tra oggetti di una certa classe. È molto utile quando vuoi ordinare liste o collezioni di oggetti secondo regole diverse da quelle “ordinarie”, o quando la classe non implementa l’interfaccia Comparable. Non tutti gli oggetti si possono confrontare: un conto è ordinare rispetto a numeri, altro conto è farlo rispetto ad altri dati. Comparator serve quando devi confrontare e ordinare qualcosa che non è solo un numero, oppure vuoi un ordinamento diverso da quello naturale (es. ordinare le stringhe al contrario, o gli oggetti per un campo particolare).
Pensiamo, ad esempio, ad ordinare delle date, date insieme a ore e via dicendo. Il Comparator si usa soprattutto quando devi ordinare oggetti non numerici o quando l’ordinamento non si basa semplicemente su numeri. Per esempio:
Oggetti complessi come classi personalizzate (
Persona,Libro,Evento, ecc.)Stringhe (ordinamento alfabetico o personalizzato)
Date o altri tipi di dati temporali
Qualsiasi tipo di dato su cui vuoi definire un ordine specifico, che non sia “naturale” o non abbia senso confrontare solo con
>o<
Perché serve il Comparator?
Immagina di avere una lista di oggetti — ad esempio una lista di persone — e di volerli ordinare per:
- Nome alfabeticamente
- Età in ordine crescente o decrescente
- Altezza
- Qualsiasi altra proprietà complessa o multipla (es. prima per età, poi per nome)
Se la classe Persona implementa Comparable, può avere un solo “criterio naturale” di ordinamento (di solito uno solo). Ma spesso ti serve ordinare in modi diversi a seconda del contesto, ed è qui che entra in gioco il Comparator.
Comparable vs Comparator
- Comparable: l’oggetto sa come confrontarsi con un altro oggetto dello stesso tipo.
Si implementa nella classe stessa, sovrascrivendo il metodocompareTo().
Permette un solo criterio di ordinamento “naturale”. - Comparator: definisci una classe o una lambda esterna che indica come ordinare due oggetti.
Puoi avere più Comparator diversi per la stessa classe, per ordinamenti diversi.
Il Comparator è uno strumento potente e fondamentale per gestire ordinamenti in Java, specialmente quando:
- La classe non ha un ordine “naturale”
- Vuoi ordinare in modi diversi in momenti diversi
- Hai bisogno di ordinamenti complessi o multi-criterio
Capire bene il Comparator ti apre molte porte nella manipolazione e organizzazione di dati in Java, migliorando la flessibilità e la chiarezza del tuo codice.
Dove si usa il Comparator?
- In metodi come
Collections.sort(List<T>, Comparator<? super T>) - Nel costruttore di collezioni ordinate, come
TreeSetoTreeMap, che usano un Comparator per mantenere l’ordine - Nel metodo
List.sort(Comparator<? super T>)di Java 8+ - Ovunque ti serva confrontare due oggetti secondo regole personalizzate
Sintassi del Comparator
L’interfaccia Comparator<T> ha un solo metodo astratto:
int compare(T o1, T o2);
- Deve restituire un numero negativo se
o1<o2 - Zero se sono uguali
- Numero positivo se
o1>o2
Esempio base di Comparator
Supponiamo di voler ordinare una lista di stringhe in ordine inverso:
Comparator<String> inverso = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s2.compareTo(s1);
}
};
Con Java 8+:
Comparator<String> inverso = (s1, s2) -> s2.compareTo(s1);
Come si usa il Comparator?
List<String> nomi = Arrays.asList("Anna", "Marco", "Luca");
Collections.sort(nomi, inverso);
System.out.println(nomi); // [Marco, Luca, Anna]
Comparator e classi personalizzate
Per oggetti complessi, crei Comparator che confrontano proprietà specifiche:
class Persona {
String nome;
int età;
// costruttore, getter, setter...
}
// Ordina per età crescente
Comparator<Persona> perEtà = (p1, p2) -> Integer.compare(p1.età, p2.età);
Comparator multipli: ordinamento combinato
Puoi combinare Comparator per ordinamenti più complessi, con metodi come:
thenComparing()reversed()
Esempio:
Comparator<Persona> perNome = Comparator.comparing(p -> p.nome);
Comparator<Persona> perEtàENome = perEtà.thenComparing(perNome);
Collections.sort(persone, perEtàENome);
Vantaggi di usare Comparator
- Flessibilità: puoi definire tanti ordinamenti diversi
- Separazione del criterio di ordinamento dalla classe
- Uso con lambda e reference methods (Java 8+) rende il codice leggibile e compatto
- Facilita la creazione di collezioni ordinate custom
1. Oggetto da ordinare: Persona
Persona.java
public class Persona {
String nome;
int età;
public Persona(String nome, int età) {
this.nome = nome;
this.età = età;
}
public void stampa() {
System.out.println(nome + " ha " + età + " anni.");
}
}
2. Lista di persone da ordinare
Main.java
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class Main {
public static void main(String[] args) {
ArrayList<Persona> persone = new ArrayList<>();
persone.add(new Persona("Anna", 30));
persone.add(new Persona("Marco", 22));
persone.add(new Persona("Luca", 27));
// Ordina per età (dal più giovane al più vecchio)
Collections.sort(persone, new Comparator<Persona>() {
@Override
public int compare(Persona p1, Persona p2) {
return Integer.compare(p1.età, p2.età);
}
});
System.out.println("Ordinate per età:");
for (Persona p : persone) {
p.stampa();
}
// Ordina per nome (alfabetico)
Collections.sort(persone, new Comparator<Persona>() {
@Override
public int compare(Persona p1, Persona p2) {
return p1.nome.compareTo(p2.nome);
}
});
System.out.println("\nOrdinate per nome:");
for (Persona p : persone) {
p.stampa();
}
}
}
✅ Risultato
Output simile a questo:
Ordinate per età:
Marco ha 22 anni.
Luca ha 27 anni.
Anna ha 30 anni.
Ordinate per nome:
Anna ha 30 anni.
Luca ha 27 anni.
Marco ha 22 anni.
Versione semplificata (con lambda)
Con Java 8+, puoi scrivere i Comparator così:
Collections.sort(persone, (p1, p2) -> p1.nome.compareTo(p2.nome));
Oppure:
persone.sort((p1, p2) -> Integer.compare(p1.età, p2.età));
