Eğitim Portalı/Java/Comparator (Esnek ve Çoklu Sıralama)
Java01-java/44-comparator

Comparator (Esnek ve Çoklu Sıralama)

Comparable bir tipe tek bir doğal sıra kazandırır. Ama gerçek hayatta aynı veriyi farklı zamanlarda farklı ölçütlere göre sıralamak isteriz: ürünleri kâh fiyata, kâh ada, kâh stoğa göre. Sınıfın içine her sıralamayı y…

Comparator (Esnek ve Çoklu Sıralama)

Comparable bir tipe tek bir doğal sıra kazandırır. Ama gerçek hayatta aynı veriyi farklı zamanlarda farklı ölçütlere göre sıralamak isteriz: ürünleri kâh fiyata, kâh ada, kâh stoğa göre. Sınıfın içine her sıralamayı yazmak mümkün değil. İşte Comparator tam da bunun içindir: sıralama mantığını sınıfın dışında, ayrı bir nesne olarak tanımlar; böylece aynı tip için istediğin kadar alternatif sıra üretirsin — hiç sınıfı değiştirmeden.

compare: iki nesneyi karşılaştır

Comparator<T> tek soyut metot ister: int compare(T a, T b) — dönüş işareti compareTo ile aynı kuralı izler (negatif a<b, 0 eşit, pozitif a>b). Functional interface olduğu için lambda ile yazılır. Ama asıl güç, hazır fabrika metotlarındadır:

Comparator.comparing(Urun::ad)               // ada göre
Comparator.comparingDouble(Urun::fiyat)      // fiyata göre (primitive, kutulamasız)
Comparator.comparingDouble(Urun::fiyat).reversed()  // azalan

Örnek 1 (./Ornek1.java) aynı ürün listesini ada, fiyata (artan/azalan) ve stoğa göre sıralar — hepsi sınıfı değiştirmeden.

Çok seviyeli sıralama: thenComparing

Birden çok ölçüt gerektiğinde thenComparing ile zincir kurarsın: ilk ölçüt eşitse ikinciye geçilir. Her seviyeyi ayrı ayrı ters çevirebilirsin:

Comparator<Calisan> sira = Comparator
    .comparing(Calisan::departman)                              // 1) departman ↑
    .thenComparing(Comparator.comparingInt(Calisan::maas).reversed()) // 2) maaş ↓
    .thenComparing(Calisan::ad);                                // 3) ad ↑

Örnek 2 (./Ornek2.java) bunu ve null güvenliğini gösterir: nullsFirst/nullsLast, null içeren listelerde NullPointerException'ı önler; naturalOrder()/reverseOrder() hazır sıralardır.

Comparable ile birlikte

İkisi birbirini tamamlar:

  • Comparable → tipin varsayılan sırası. stream.sorted() (argümansız) ve Collections.sort(list) bunu kullanır.
  • Comparator → o anki ihtiyaca göre varsayılanı ezen esnek sıra. stream.sorted(cmp), list.sort(cmp) ve TreeSet/TreeMap yapıcısına verilir.

Örnek 3 (./Ornek3.java) puana göre doğal sıralı bir Oyuncu tanımlar, sonra bir Comparator'la "puan ↓, süre ↑" liderlik sırasına geçer ve TreeMap'e özel sıra verir.

Nerede kullanılır?

  • Stream: .sorted(comparator) — veri işleme hatlarında.
  • Koleksiyonlar: list.sort(cmp), TreeSet/TreeMap (cmp ver), PriorityQueue (öncelik sırası).
  • min/max: list.stream().max(Comparator.comparing(...)).
  • Gruplama sonrası: rapor/tablo sıralamaları (en çok satan, en yeni...).
ComparableComparator
NeredeSınıfın içinde (compareTo)Sınıfın dışında (ayrı nesne)
Kaç sıraTek (doğal)Sınırsız (alternatif)
Sınıfı değiştirir miEvetHayır
TipikVarsayılan sıraÖzel/çoklu sıra

Özet

Comparator ile sınıfı değiştirmeden esnek ve çoklu sıralamayı: fabrika metotlarını (comparing/comparingInt/reversed) (Örnek 1); çok seviyeli thenComparing ve null güvenliğini (Örnek 2); Comparable ile birlikte stream/TreeMap kullanımını (Örnek 3) öğrendik. "Tek doğal sıra → Comparable, çok/özel sıra → Comparator" kuralı işini görür. Sırada, Iterator'ın atası ve eski koleksiyonlar: Enumeration ve legacy yapılar.

Kod Örnekleri(3)

Ornek1

çalıştırılabilir
Ornek1.java
1// Ornek1: Comparator — sınıfı değiştirmeden, dışarıdan farklı sıralamalar tanımlamak.
2// Çalıştırma: java Ornek1.java
3import java.util.ArrayList;
4import java.util.Comparator;
5import java.util.List;
6
7public class Ornek1 {
8
9    record Urun(String ad, double fiyat, int stok) {}
10
11    public static void main(String[] args) {
12        List<Urun> urunler = new ArrayList<>(List.of(
13                new Urun("Monitör", 3200, 5),
14                new Urun("Mouse", 250, 40),
15                new Urun("Klavye", 450, 12),
16                new Urun("Kulaklık", 900, 12)));
17
18        // Comparator dışarıdan tanımlanır; aynı listeyi farklı ölçütlerle sıralayabiliriz.
19
20        // 1) Ada göre (alfabetik) — Comparator.comparing + anahtar çıkarıcı
21        urunler.sort(Comparator.comparing(Urun::ad));
22        System.out.println("Ada göre   : " + urunler.stream().map(Urun::ad).toList());
23
24        // 2) Fiyata göre artan
25        urunler.sort(Comparator.comparingDouble(Urun::fiyat));
26        System.out.println("Fiyat artan: " + urunler.stream().map(Urun::ad).toList());
27
28        // 3) Fiyata göre AZALAN — reversed()
29        urunler.sort(Comparator.comparingDouble(Urun::fiyat).reversed());
30        System.out.println("Fiyat azalan: " + urunler.stream().map(Urun::ad).toList());
31
32        // 4) Lambda ile elle: stoğa göre
33        urunler.sort((a, b) -> Integer.compare(a.stok(), b.stok()));
34        System.out.println("Stok artan : " + urunler.stream().map(Urun::ad).toList());
35
36        System.out.println("""
37
38                --- Comparator ---
39                Comparator, sıralama mantığını sınıfın DIŞINDA tutar; aynı tip için BİRDEN ÇOK sıra tanımlanır.
40                compare(a,b): negatif (a<b), 0 (eşit), pozitif (a>b).
41                Kısa yollar: Comparator.comparing(anahtar), comparingInt/Double, .reversed().
42                Sınıfı hiç değiştirmeden, ihtiyaca göre kâh ada kâh fiyata göre sıralarsın.""");
43    }
44}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek2

çalıştırılabilir
Ornek2.java
1// Ornek2: Çok seviyeli sıralama (thenComparing) ve null güvenliği.
2// Çalıştırma: java Ornek2.java
3import java.util.ArrayList;
4import java.util.Arrays;
5import java.util.Comparator;
6import java.util.List;
7
8public class Ornek2 {
9
10    record Calisan(String departman, String ad, int maas) {}
11
12    public static void main(String[] args) {
13        List<Calisan> liste = new ArrayList<>(List.of(
14                new Calisan("Yazılım", "Ada", 50000),
15                new Calisan("Yazılım", "Burak", 60000),
16                new Calisan("Satış", "Can", 40000),
17                new Calisan("Yazılım", "Ada", 55000)));
18
19        // Çok seviyeli: önce departman (artan), eşitse maaş (AZALAN), eşitse ad.
20        Comparator<Calisan> sira = Comparator
21                .comparing(Calisan::departman)
22                .thenComparing(Comparator.comparingInt(Calisan::maas).reversed())
23                .thenComparing(Calisan::ad);
24
25        liste.sort(sira);
26        System.out.println("Departman ↑, maaş ↓, ad ↑:");
27        liste.forEach(c -> System.out.println("  " + c));
28
29        // null güvenliği: nullsFirst / nullsLast
30        List<String> adlar = new ArrayList<>(Arrays.asList("Zeynep", null, "Ahmet", null, "Berk"));
31        adlar.sort(Comparator.nullsFirst(Comparator.naturalOrder()));
32        System.out.println("\nnull'lar önce: " + adlar);
33
34        adlar.sort(Comparator.nullsLast(Comparator.reverseOrder()));
35        System.out.println("null'lar sonda, ters: " + adlar);
36
37        System.out.println("""
38
39                --- thenComparing ve null güvenliği ---
40                thenComparing: ilk ölçüt eşitse İKİNCİ ölçüte geç (zincir). İstediğin kadar seviye ekleyebilirsin.
41                Her seviyeyi ayrı ayrı .reversed() ile ters çevirebilirsin (örn. departman ↑ ama maaş ↓).
42                naturalOrder()/reverseOrder(): hazır doğal/ters sıra.
43                nullsFirst/nullsLast: null içeren listelerde NullPointerException'ı önler.""");
44    }
45}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek3

çalıştırılabilir
Ornek3.java
1// Ornek3: Comparable + Comparator birlikte; stream.sorted ve TreeMap ile özel sıra.
2// Çalıştırma: java Ornek3.java
3import java.util.Comparator;
4import java.util.List;
5import java.util.TreeMap;
6
7public class Ornek3 {
8
9    // Sınıfın DOĞAL sırası (Comparable): puana göre azalan (liderlik tablosu mantığı).
10    record Oyuncu(String ad, int puan, int sure) implements Comparable<Oyuncu> {
11        @Override public int compareTo(Oyuncu o) { return Integer.compare(o.puan, this.puan); } // azalan
12    }
13
14    public static void main(String[] args) {
15        List<Oyuncu> oyuncular = List.of(
16                new Oyuncu("Ada", 90, 120),
17                new Oyuncu("Burak", 90, 95),
18                new Oyuncu("Can", 75, 60),
19                new Oyuncu("Derya", 88, 140));
20
21        // 1) Doğal sıra (Comparable): puan azalan — stream.sorted() argümansız.
22        System.out.println("Doğal sıra (puan ↓):");
23        oyuncular.stream().sorted().forEach(o -> System.out.println("  " + o));
24
25        // 2) Comparator ile EZ: puan azalan, eşitse süre artan (daha hızlı bitiren önde).
26        Comparator<Oyuncu> liderlik = Comparator
27                .comparingInt(Oyuncu::puan).reversed()
28                .thenComparingInt(Oyuncu::sure);
29        System.out.println("\nÖzel sıra (puan ↓, süre ↑):");
30        oyuncular.stream().sorted(liderlik).forEach(o -> System.out.println("  " + o));
31
32        // 3) TreeMap'e özel Comparator vererek anahtarları o sıraya göre tut (uzun string önce).
33        TreeMap<String, Integer> harita = new TreeMap<>(Comparator.comparingInt(String::length).reversed()
34                .thenComparing(Comparator.naturalOrder()));
35        oyuncular.forEach(o -> harita.put(o.ad(), o.puan()));
36        System.out.println("\nTreeMap (ada göre uzunluk ↓): " + harita.keySet());
37
38        System.out.println("""
39
40                --- Comparable + Comparator birlikte ---
41                Comparable: tipin tek 'varsayılan' sırası (stream.sorted() argümansız bunu kullanır).
42                Comparator: o anki ihtiyaca göre, varsayılanı EZEN esnek sıralar (stream.sorted(cmp)).
43                Comparator ayrıca TreeSet/TreeMap'e verilebilir; koleksiyon elemanlarını o sıraya göre tutar.
44                Pratik kural: tek doğal sıra -> Comparable; çok/özel sıra -> Comparator.""");
45    }
46}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.