Ce face Comparatori (Comparable și Comparator)
Ca să sortezi obiecte, Java trebuie să știe cum se compară două. Metoda de comparare returnează un număr negativ dacă primul vine înainte, 0 dacă sunt egale și pozitiv dacă vine după. Clasele care implementează Comparable<T> au o ordine naturală (String alfabetic după codul caracterelor, numerele crescător, datele cronologic), folosită de Collections.sort, TreeMap și TreeSet.
Un Comparator<T> este un obiect separat care descrie o ordine, oricâte vrei pentru aceeași clasă. Metodele statice și implicite construiesc comparatori fără cod lung: Comparator.comparing(Elev::nume), comparingDouble, thenComparing (criteriu secundar), reversed() (ordine inversă), Comparator.nullsFirst(...), Comparator.reverseOrder().
Pentru numere folosește Integer.compare(a, b) sau Double.compare(a, b), nu a - b (poate depăși capacitatea). Pentru texte în română cu diacritice, ordinea naturală pune „Ș” după „Z”; un Collator pentru limba română ordonează corect.
Exemple
Exemplul 1: Ordine naturală și comparatori combinați
Ordinea naturală a lui Elev este după nume. Al doilea criteriu sortează descrescător după medie, iar la medii egale, alfabetic. Al treilea sortează după clasă, iar în aceeași clasă, cu Comparator.reverseOrder(), invers față de ordinea naturală (de la Z la A).
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
record Elev(String nume, double medie, int clasa) implements Comparable<Elev> {
@Override
public int compareTo(Elev altul) {
return nume.compareTo(altul.nume);
}
}
public class Main {
public static void main(String[] args) {
List<Elev> elevi = new ArrayList<>(List.of(
new Elev("Radu", 9.10, 10), new Elev("Ana", 9.75, 11),
new Elev("Ioana", 9.10, 10), new Elev("Bogdan", 8.40, 11)));
Collections.sort(elevi);
elevi.forEach(e -> System.out.print(e.nume() + " "));
System.out.println("(alfabetic)");
elevi.sort(Comparator.comparingDouble(Elev::medie).reversed().thenComparing(Elev::nume));
elevi.forEach(e -> System.out.println(e.medie() + " " + e.nume()));
elevi.sort(Comparator.comparingInt(Elev::clasa).thenComparing(Comparator.reverseOrder()));
System.out.println(elevi.stream().map(e -> e.clasa() + ":" + e.nume()).toList());
}
}
Rezultat
Ana Bogdan Ioana Radu (alfabetic)
9.75 Ana
9.1 Ioana
9.1 Radu
8.4 Bogdan
[10:Radu, 10:Ioana, 11:Bogdan, 11:Ana]
Exemplul 2: Valori null și texte cu diacritice
nullsLast pune orașele lipsă la final. Ordinea naturală a lui String pune „Ștefan” și „Țara” după „Zalău”; Collator pentru română le așază corect.
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
List<String> orase = new ArrayList<>(Arrays.asList("Sibiu", null, "Arad", "Bacău", null));
orase.sort(Comparator.nullsLast(Comparator.naturalOrder()));
System.out.println(orase);
List<String> nume = new ArrayList<>(List.of("Zalău", "Ștefan", "Sorin", "Țara", "Tudor", "Ana"));
nume.sort(Comparator.naturalOrder());
System.out.println("natural: " + nume);
Collator romana = Collator.getInstance(Locale.forLanguageTag("ro-RO"));
nume.sort(romana);
System.out.println("română: " + nume);
}
}
Rezultat
[Arad, Bacău, Sibiu, null, null]
natural: [Ana, Sorin, Tudor, Zalău, Ștefan, Țara]
română: [Ana, Sorin, Ștefan, Țara, Tudor, Zalău]
Greșeli frecvente
- Compari cu scădere:
return a.varsta - b.varsta;. Pentru valori mari scăderea depășeșteintși ordinea iese greșită. FoloseșteInteger.compare. - Scrii un
compareTocare nu e de acord cuequals:TreeSetconsideră egale (și elimină) obiectele pentru care comparatorul dă 0, chiar dacă sunt diferite. - Pui
reversed()la final, dupăthenComparing, și inversezi tot lanțul, nu doar primul criteriu. Ordinea apelurilor contează. - Sortezi cuvinte românești cu ordinea naturală și „Ș” ajunge după „Z”. Folosește
Collator.getInstance(Locale.forLanguageTag("ro-RO")).
Comenzi înrudite
Interfețe (interface, implements): O interfață (interface) descrie ce metode trebuie să aibă o clasă; clasa o promite cu implements și poate implementa mai multe interfețe.Referințe la metode (::): O referință la metodă (Clasa::metoda) este o lambda prescurtată care doar apelează o metodă existentă.Expresii lambda: O lambda este o funcție scurtă, fără nume, scrisă ca parametri -> corp, folosită oriunde se așteaptă o interfață funcțională.equals și hashCode: equals decide când două obiecte sunt „egale” ca valoare; hashCode trebuie rescris împreună cu el, altfel HashMap și HashSet nu funcționează corect.Comparator: Interfața care spune cum se ordonează două obiecte: compare(a, b) returnează un număr negativ, zero sau pozitiv.record: record declară o clasă imutabilă pentru date: constructorul, accesorii, equals, hashCode și toString sunt generați automat.