Eğitim Portalı/Java/Comparable Arayüzü (Doğal Sıralama)
Java01-java/43-comparable-arayuzu

Comparable Arayüzü (Doğal Sıralama)

Nesneleri sıralamak istediğimizde, Java'nın "hangisi önce gelir?" sorusunu yanıtlaması gerekir. Sayılar için bu açıktır (küçükten büyüğe), String için alfabetiktir. Peki kendi sınıfların — Urun, Kisi? İşte burada Comp…

Comparable Arayüzü (Doğal Sıralama)

Nesneleri sıralamak istediğimizde, Java'nın "hangisi önce gelir?" sorusunu yanıtlaması gerekir. Sayılar için bu açıktır (küçükten büyüğe), String için alfabetiktir. Peki kendi sınıfların — Urun, Kisi? İşte burada Comparable devreye girer: bir sınıfa doğal sıra (natural ordering) kazandırır, yani "bu tipin varsayılan sıralaması şudur" der.

compareTo: tek metot

Comparable<T> tek metot ister: int compareTo(T o). Dönüş değerinin işareti önemlidir:

  • negatif → bu nesne, o'dan önce gelir (this < o)
  • sıfır → eşit sıradalar
  • pozitif → bu nesne, o'dan sonra gelir (this > o)
class Urun implements Comparable<Urun> {
    private double fiyat;
    public int compareTo(Urun o) {
        return Double.compare(this.fiyat, o.fiyat);  // doğal sıra: fiyat artan
    }
}

İpucu: Karşılaştırmayı elle this.x - o.x ile yapma (taşma/NaN riski). Bunun yerine Integer.compare, Double.compare, String.compareTo gibi hazır metotları kullan.

Doğal sırayı kim kullanır?

Comparable uygulayan bir tip, Java'nın tüm sıralama altyapısıyla otomatik çalışır:

  • Collections.sort(list) ve list.sort(null)
  • Arrays.sort(dizi)
  • TreeSet ve TreeMap (elemanları sıralı tutarlar)
  • Collections.min/max, sıralı stream işlemleri

Örnek 1 (./Ornek1.java) fiyata göre doğal sıralı bir Urun tanımlar; sort, TreeSet.first()/ last() ile en ucuz/en pahalıyı bulur.

Çok alanlı sıralama

Genelde tek alan yetmez: önce soyada, eşitse ada, o da eşitse yaşa göre sırala. Bunu compareTo içinde zincirleme yaparsın — ilk sıfır-olmayan sonuç kazanır:

public int compareTo(Kisi o) {
    int s = soyad.compareTo(o.soyad);  if (s != 0) return s;
    int a = ad.compareTo(o.ad);        if (a != 0) return a;
    return Integer.compare(yas, o.yas);
}

Örnek 2 (./Ornek2.java) üç alana göre sıralar.

Sözleşme (contract) — ihlal etme!

compareTo belirli kurallara uymalıdır, yoksa sıralama ve TreeSet/TreeMap bozulur:

  • Anti-simetri: a.compareTo(b) ile b.compareTo(a) ters işaretli olmalı.
  • Geçişlilik: a<b ve b<c ise a<c.
  • equals ile uyum (önerilir): compareTo == 0 olduğunda equals == true olmalı. Çünkü TreeSet/TreeMap eşitliği equals ile değil compareTo ile belirler; uyumsuzluk "kümede var ama bulunamıyor" gibi sürprizlere yol açar. Bu yüzden Örnek 2'de equals/hashCode, compareTo ile aynı alanları kullanır.

Comparable mı, Comparator mı?

  • Comparable: Tipin tek, varsayılan sırası. Sınıfın içine yazılır (compareTo). "Bu tipin doğal sırası budur."
  • Comparator: Dışarıdan, çok sayıda alternatif sıra. Sınıfı değiştirmeden farklı ölçütlerle sıralamak için (bir sonraki konu). Örn. ürünleri kâh fiyata kâh ada göre sıralamak.

İkisi birlikte kullanılır: doğal sıra Comparable ile, özel/çoklu sıralar Comparator ile.

Özet

Comparable ile bir tipe doğal sıra kazandırmayı: compareTo'nun işaret kuralını, hazır compare metotlarını, doğal sırayı kullanan altyapıyı (sort, TreeSet) (Örnek 1); çok alanlı zincirleme karşılaştırmayı ve equals ile uyum sözleşmesini (Örnek 2) öğrendik. Sırada, sınıfı değiştirmeden esnek ve çoklu sıralama: Comparator.

Kod Örnekleri(2)

Ornek1

çalıştırılabilir
Ornek1.java
1// Ornek1: Comparable — bir nesneye "doğal sıra" (natural ordering) kazandırmak.
2// Çalıştırma: java Ornek1.java
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.List;
6import java.util.TreeSet;
7
8public class Ornek1 {
9
10    public static void main(String[] args) {
11        List<Urun> urunler = new ArrayList<>(List.of(
12                new Urun("Monitör", 3200), new Urun("Mouse", 250),
13                new Urun("Klavye", 450), new Urun("Kulaklık", 900)));
14
15        // Urun, Comparable uyguladığı için doğrudan sıralanabilir (doğal sıra: fiyata göre).
16        Collections.sort(urunler);
17        System.out.println("Doğal sıra (fiyat artan):");
18        urunler.forEach(u -> System.out.println("  " + u));
19
20        // list.sort(null) da doğal sırayı kullanır:
21        urunler.sort(null);
22
23        // TreeSet doğal sırayı kullanarak elemanları SIRALI tutar (Comparable şart).
24        TreeSet<Urun> sirali = new TreeSet<>(urunler);
25        System.out.println("\nTreeSet (en ucuz): " + sirali.first());
26        System.out.println("TreeSet (en pahalı): " + sirali.last());
27
28        System.out.println("""
29
30                --- Comparable (doğal sıra) ---
31                Comparable<T> uygulayan sınıf 'doğal' bir sıraya sahip olur: compareTo(o) metodu.
32                Dönüş: negatif (bu < o), 0 (eşit), pozitif (bu > o).
33                Collections.sort, list.sort(null), TreeSet, TreeMap, Arrays.sort bunu kullanır.
34                Tek bir 'varsayılan' sıralama için idealdir (ör. sayılar artan, String alfabetik).""");
35    }
36}
37
38// Comparable: bu sınıfın nesneleri kendi aralarında karşılaştırılabilir.
39class Urun implements Comparable<Urun> {
40    private final String ad;
41    private final double fiyat;
42    Urun(String ad, double fiyat) { this.ad = ad; this.fiyat = fiyat; }
43
44    @Override
45    public int compareTo(Urun diger) {
46        // Doğal sıra = fiyata göre artan. (Double.compare taşma/NaN güvenli.)
47        return Double.compare(this.fiyat, diger.fiyat);
48    }
49
50    @Override public String toString() { return ad + " (" + fiyat + " TL)"; }
51}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek2

çalıştırılabilir
Ornek2.java
1// Ornek2: Çok alanlı compareTo ve equals ile tutarlılık.
2// Çalıştırma: java Ornek2.java
3import java.util.ArrayList;
4import java.util.List;
5import java.util.Objects;
6
7public class Ornek2 {
8
9    public static void main(String[] args) {
10        List<Kisi> kisiler = new ArrayList<>(List.of(
11                new Kisi("Yılmaz", "Ada", 30),
12                new Kisi("Yılmaz", "Burak", 25),
13                new Kisi("Demir", "Ada", 40),
14                new Kisi("Demir", "Ada", 22)));
15
16        kisiler.sort(null); // doğal sıra: soyad -> ad -> yaş
17        System.out.println("Çok alanlı doğal sıra (soyad, ad, yaş):");
18        kisiler.forEach(k -> System.out.println("  " + k));
19
20        System.out.println("""
21
22                --- Çok alanlı compareTo ve sözleşme ---
23                Birden çok ölçüte göre sıralamak için compareTo'da ZİNCİRLEME karşılaştırma yap:
24                önce soyad; eşitse ad; o da eşitse yaş. İlk sıfır-olmayan sonuç kazanır.
25                Sözleşme kuralları:
26                  - işaret tutarlı (a<b ise b>a), geçişli (a<b, b<c => a<c),
27                  - mümkünse equals ile UYUMLU: compareTo==0 oldugunda equals==true olmalı
28                    (TreeSet/TreeMap eşitliği compareTo ile belirler; uyumsuzluk sürprizlere yol açar).""");
29    }
30}
31
32class Kisi implements Comparable<Kisi> {
33    private final String soyad, ad;
34    private final int yas;
35    Kisi(String soyad, String ad, int yas) { this.soyad = soyad; this.ad = ad; this.yas = yas; }
36
37    @Override
38    public int compareTo(Kisi o) {
39        int s = this.soyad.compareTo(o.soyad);       // 1) soyada göre
40        if (s != 0) return s;
41        int a = this.ad.compareTo(o.ad);             // 2) eşitse ada göre
42        if (a != 0) return a;
43        return Integer.compare(this.yas, o.yas);     // 3) o da eşitse yaşa göre
44    }
45
46    // compareTo ile UYUMLU equals/hashCode (aynı üç alan).
47    @Override public boolean equals(Object o) {
48        return o instanceof Kisi k && yas == k.yas
49                && soyad.equals(k.soyad) && ad.equals(k.ad);
50    }
51    @Override public int hashCode() { return Objects.hash(soyad, ad, yas); }
52    @Override public String toString() { return soyad + " " + ad + " (" + yas + ")"; }
53}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.