Interfețele Comparator și Comparable

Cuprins

Interfața Comparable

Interfața Comparable face parte din pachetul java.lang și permite obiectelor unei clase să definească ordinea lor "naturală". Implementând această interfață, obiectele unei clase pot fi sortate automat folosind metodele de sortare din Collections sau Arrays.

Semnătura interfeței:

public interface Comparable<T> {
    public int compareTo(T o);
}

Metoda compareTo():

  • Returnează un număr negativ dacă obiectul curent este mai mic decât obiectul furnizat ca parametru

  • Returnează zero dacă obiectul curent este egal cu obiectul furnizat

  • Returnează un număr pozitiv dacă obiectul curent este mai mare decât obiectul furnizat

Exemplu de implementare:

public class Student implements Comparable<Student> {
    private String nume;
    private int varsta;
    private double medie;
    
    public Student(String nume, int varsta, double medie) {
        this.nume = nume;
        this.varsta = varsta;
        this.medie = medie;
    }
    
    // Getteri și setteri
    public String getNume() { return nume; }
    public int getVarsta() { return varsta; }
    public double getMedie() { return medie; }
    
    @Override
    public int compareTo(Student altStudent) {
        // Comparăm după medie (ordinea naturală)
        return Double.compare(this.medie, altStudent.medie);
        
        // Alternativ, pentru comparare descrescătoare:
        // return Double.compare(altStudent.medie, this.medie);
    }
    
    @Override
    public String toString() {
        return nume + " (vârsta: " + varsta + ", medie: " + medie + ")";
    }
}

Utilizare:

List<Student> studenti = new ArrayList<>();
studenti.add(new Student("Ana", 20, 8.5));
studenti.add(new Student("Mihai", 22, 9.2));
studenti.add(new Student("Elena", 21, 7.8));

// Sortare folosind ordinea naturală definită în compareTo
Collections.sort(studenti);

// Afișare studenti sortați după medie
studenti.forEach(System.out::println);

Interfața Comparator

Interfața Comparator face parte din pachetul java.util și oferă un mecanism de comparare extern pentru obiectele unei clase. Spre deosebire de Comparable, un Comparator nu necesită modificarea clasei obiectelor pe care le compară.

Semnătura interfeței:

public interface Comparator<T> {
    int compare(T o1, T o2);
}

Metoda compare():

  • Returnează un număr negativ dacă primul obiect este mai mic decât al doilea

  • Returnează zero dacă obiectele sunt egale

  • Returnează un număr pozitiv dacă primul obiect este mai mare decât al doilea

Exemplu de implementare:

// Comparator pentru sortarea studenților după nume
public class StudentNumeComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        return s1.getNume().compareTo(s2.getNume());
    }
}

// Comparator pentru sortarea studenților după vârstă
public class StudentVarstaComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        return Integer.compare(s1.getVarsta(), s2.getVarsta());
    }
}

Utilizare:

List<Student> studenti = new ArrayList<>();
studenti.add(new Student("Ana", 20, 8.5));
studenti.add(new Student("Mihai", 22, 9.2));
studenti.add(new Student("Elena", 21, 7.8));

// Sortare folosind un Comparator pentru nume
Collections.sort(studenti, new StudentNumeComparator());
System.out.println("Sortați după nume:");
studenti.forEach(System.out::println);

// Sortare folosind un Comparator pentru vârstă
Collections.sort(studenti, new StudentVarstaComparator());
System.out.println("\nSortați după vârstă:");
studenti.forEach(System.out::println);

Diferențe între Comparable și Comparator

Criteriu
Comparable
Comparator

Pachet

java.lang

java.util

Metoda

compareTo()

compare()

Modifică clasa

Da

Nu

Ordine

Definește o singură ordine naturală

Permite multiple criterii de sortare

Utilizare

Integrată cu clasa

Externă, independentă de clasă

Sintaxă

obj1.compareTo(obj2)

comparator.compare(obj1, obj2)

Utilizarea Comparator cu colecții

Comparatori anonimi

În Java modern, putem folosi clase anonime sau expresii lambda pentru a crea comparatori:

// Folosind clasă anonimă
Collections.sort(studenti, new Comparator<Student>() {
    @Override
    public int compare(Student s1, Student s2) {
        return Double.compare(s1.getMedie(), s2.getMedie());
    }
});

// Folosind expresie lambda (Java 8+)
Collections.sort(studenti, (s1, s2) -> Double.compare(s1.getMedie(), s2.getMedie()));

// Folosind referințe de metode și Comparator.comparing()
Collections.sort(studenti, Comparator.comparing(Student::getMedie));

Comparatori compuși

Începând cu Java 8, interfața Comparator include metode default pentru combinarea comparatorilor:

// Sortare după medie (descrescător) și apoi după nume (crescător)
Comparator<Student> comparator = Comparator.comparing(Student::getMedie).reversed()
                                         .thenComparing(Student::getNume);

Collections.sort(studenti, comparator);

Comparatori cu TreeSet și TreeMap

TreeSet și TreeMap folosesc comparatori pentru a sorta elementele:

// TreeSet cu comparator personalizat
Set<Student> studentSet = new TreeSet<>(Comparator.comparing(Student::getNume));
studentSet.addAll(studenti);

// TreeMap cu comparator personalizat
Map<Student, String> studentMap = new TreeMap<>(Comparator.comparing(Student::getVarsta));
for (Student s : studenti) {
    studentMap.put(s, s.getNume() + " - Grupa A");
}

Exemple practice

Exemplu complet cu Comparable:

import java.util.*;

class Carte implements Comparable<Carte> {
    private String titlu;
    private String autor;
    private int anPublicare;
    
    public Carte(String titlu, String autor, int anPublicare) {
        this.titlu = titlu;
        this.autor = autor;
        this.anPublicare = anPublicare;
    }
    
    // Getteri
    public String getTitlu() { return titlu; }
    public String getAutor() { return autor; }
    public int getAnPublicare() { return anPublicare; }
    
    @Override
    public int compareTo(Carte altaCarte) {
        // Sortare după an de publicare
        return Integer.compare(this.anPublicare, altaCarte.anPublicare);
    }
    
    @Override
    public String toString() {
        return titlu + " de " + autor + " (" + anPublicare + ")";
    }
}

public class ExempluComparable {
    public static void main(String[] args) {
        List<Carte> carti = new ArrayList<>();
        carti.add(new Carte("Moara cu noroc", "Ioan Slavici", 1881));
        carti.add(new Carte("Ion", "Liviu Rebreanu", 1920));
        carti.add(new Carte("Enigma Otiliei", "George Călinescu", 1938));
        carti.add(new Carte("Baltagul", "Mihail Sadoveanu", 1930));
        
        Collections.sort(carti);
        
        System.out.println("Cărți sortate după anul publicării:");
        for (Carte carte : carti) {
            System.out.println(carte);
        }
    }
}

Exemplu complet cu Comparator:

import java.util.*;

class Produs {
    private String nume;
    private double pret;
    private int cantitate;
    
    public Produs(String nume, double pret, int cantitate) {
        this.nume = nume;
        this.pret = pret;
        this.cantitate = cantitate;
    }
    
    // Getteri
    public String getNume() { return nume; }
    public double getPret() { return pret; }
    public int getCantitate() { return cantitate; }
    
    @Override
    public String toString() {
        return nume + " - " + pret + " lei (stoc: " + cantitate + ")";
    }
}

// Comparator pentru sortare după preț
class PretComparator implements Comparator<Produs> {
    @Override
    public int compare(Produs p1, Produs p2) {
        return Double.compare(p1.getPret(), p2.getPret());
    }
}

// Comparator pentru sortare după cantitate
class CantitateComparator implements Comparator<Produs> {
    @Override
    public int compare(Produs p1, Produs p2) {
        return Integer.compare(p1.getCantitate(), p2.getCantitate());
    }
}

public class ExempluComparator {
    public static void main(String[] args) {
        List<Produs> produse = new ArrayList<>();
        produse.add(new Produs("Laptop", 3200.50, 5));
        produse.add(new Produs("Telefon", 1500.00, 10));
        produse.add(new Produs("Tabletă", 900.75, 7));
        produse.add(new Produs("Căști", 150.99, 20));
        
        // Sortare folosind PretComparator
        Collections.sort(produse, new PretComparator());
        System.out.println("Produse sortate după preț:");
        for (Produs p : produse) {
            System.out.println(p);
        }
        
        System.out.println();
        
        // Sortare folosind CantitateComparator
        Collections.sort(produse, new CantitateComparator());
        System.out.println("Produse sortate după cantitate:");
        for (Produs p : produse) {
            System.out.println(p);
        }
        
        System.out.println();
        
        // Sortare folosind comparator lambda (Java 8+)
        produse.sort((p1, p2) -> p1.getNume().compareTo(p2.getNume()));
        System.out.println("Produse sortate după nume (lambda):");
        produse.forEach(System.out::println);
        
        System.out.println();
        
        // Sortare folosind Comparator.comparing (Java 8+)
        produse.sort(
            Comparator.comparing(Produs::getPret)
                      .reversed()
                      .thenComparing(Produs::getNume)
        );
        System.out.println("Produse sortate după preț (descrescător) și apoi după nume:");
        produse.forEach(System.out::println);
    }
}

Last updated