---
title: Sözlükler
source: 'https://academia.sh/tr/kurslar/java-standart-kutuphane/sozlukler'
course: 'Standart Kütüphane ve Akışlar'
language: tr
updated: '2026-08-17T18:09:42+00:00'
license: 'CC BY-SA 4.0'
---

# Sözlükler

Anahtar-değer sözlüğünün üç sözü — anahtarın tekilliği, anahtar görünümünün canlılığı, yok anahtarın boş değer vermesi — üç gerçekleştirimde de aynı çıkıyor ve arayüze ait oluyor. Gezinme sırası, boş anahtar kabulü ve anahtarın karşılaştırılabilir olma zorunluluğu ise karma ile sıralı gerçekleştirim arasında ayrışıyor. Çağıranın kuralı ilk kez merkeze alınıyor: equals yazıp hashCode yazmayan bir anahtarla arama hiçbir istisna vermeden boş değer döndürüyor, aynı anahtar sıralı bir sözlükte ise sessizce bulunuyor çünkü orada kullanılan söz compareTo.

Önceki ders liste, küme ve kuyruğun ögeye üç ayrı yoldan eriştiğini ölçtü: konum, üyelik,
uç. Üçü de kabın kendi kendine yettiği erişimlerdi — ögeyi bulmak için kabın dışından
hiçbir bilgi gerekmiyordu. Sözlük bunu bozuyor. Bir sözlükte ögeye anahtarıyla erişilir ve
anahtar çağıranın yazdığı, kütüphanenin hiç görmediği bir sınıf olabilir. Kap yalnız
anahtarı sorar; anahtarın kendi kendiyle tutarlı davranıp davranmadığını denetlemez. Bu
ders o tutarsızlığın nereye düştüğünü ölçüyor: hangi söz kütüphaneden geliyor, hangi söz
çağıranın yazdığı sınıfın omuzlarında duruyor, ve ikisi arasında bir çatlak açıldığında
program bunu nasıl haber veriyor — ya da vermiyor.

Önceki iki dersin ölçtüğü her kusur derlenen bir programda anında görünürdü ya da hiç
görünmezdi — istisna fırlıyorsa fırlıyordu, fırlamıyorsa davranış tutarlıydı. Sözlükteki
anahtar kusuru üçüncü bir yol açıyor: program derlenir, çalışır, hiçbir istisna
fırlatmaz, ve yine de yanlış çalışır. Bu üçüncü yol kütüphanenin bir eksikliği değil,
kütüphanenin çağırana bıraktığı bir yükümlülüğün karşılıksız kalmasıdır.

## Anahtar-Değer Tutmanın Arayüz Sözü

Sözlük de önceki iki dersteki gibi üç gerçekleştirimle (`HashMap`, `LinkedHashMap`,
`TreeMap`) sınanır; ölçüm motoru aynıdır, yalnız bu derste kullanılan sorular yenidir.

- **KO11** — Motor önceki iki dersle **anlamca birebir**; yalnız bu derste sorulan
  sorular eklenmiştir. `Garanti` sınıfına yeni bir soru eklemek var olan soruların
  davranışını değiştirmez.
- **KO12** — Sözlük beş anahtarla, aynı sırayla, aynı `doldur` yardımcısıyla doldurulur.

```java
// Garanti.java — bir davranisin garantisini kim veriyor: arayuz mu, gerceklestirim mi
import java.util.*;
import java.util.function.*;

public class Garanti {
    static int arayuz = 0;
    static int gerceklestirim = 0;

    final List<String> adlar = new ArrayList<>();
    final List<Supplier<Object>> uretecler = new ArrayList<>();

    Garanti ekle(String ad, Supplier<Object> uretec) {
        adlar.add(ad);
        uretecler.add(uretec);
        return this;
    }

    void baslik(String aile) {
        StringBuilder sb = new StringBuilder(String.format("%-32s", aile));
        for (String ad : adlar) sb.append(String.format("%-15s", ad));
        sb.append("garantiyi veren");
        System.out.println(sb);
    }

    void sor(String soru, Function<Object, String> gozlem) {
        List<String> yanitlar = new ArrayList<>();
        for (Supplier<Object> u : uretecler) {
            String y;
            try {
                y = gozlem.apply(u.get());
            } catch (RuntimeException e) {
                y = "hata";
            }
            yanitlar.add(y);
        }
        boolean ortak = new HashSet<>(yanitlar).size() == 1;
        if (ortak) arayuz++; else gerceklestirim++;
        StringBuilder sb = new StringBuilder(String.format("%-32s", soru));
        for (String y : yanitlar) sb.append(String.format("%-15s", y));
        sb.append(ortak ? "arayuz" : "gerceklestirim");
        System.out.println(sb);
    }

    static void ozet() {
        System.out.printf("gozlem: %d arayuz garantisi, %d gerceklestirim davranisi%n",
                arayuz, gerceklestirim);
    }
}
```

```java
// Sozluk.java — anahtar-deger tutmanin arayuz sozu ile karma/sirali ayrimi
import java.util.*;

public class Sozluk {
    static final List<String> KAYNAK = List.of("zulu", "delta", "alfa", "carli", "bravo");

    static Map<String, Integer> doldur(Map<String, Integer> m) {
        for (int i = 0; i < KAYNAK.size(); i++) m.put(KAYNAK.get(i), i);
        return m;
    }

    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        Garanti sozluk = new Garanti()
                .ekle("HashMap", () -> doldur(new HashMap<>()))
                .ekle("LinkedHashMap", () -> doldur(new LinkedHashMap<>()))
                .ekle("TreeMap", () -> doldur(new TreeMap<>()));
        sozluk.baslik("sozluk");
        sozluk.sor("anahtar tekil mi", o -> {
            Map<String, Integer> m = (Map<String, Integer>) o;
            m.put("alfa", 99);
            return String.valueOf(m.size());
        });
        sozluk.sor("anahtar gorunumu canli mi", o -> {
            Map<String, Integer> m = (Map<String, Integer>) o;
            Set<String> g = m.keySet();
            m.put("ekstra", 5);
            return String.valueOf(g.contains("ekstra"));
        });
        sozluk.sor("yok anahtarin yaniti ne", o -> String.valueOf(((Map<String, Integer>) o).get("yok")));
        sozluk.sor("gezinme sirasi ekleme sirasi mi",
                o -> String.valueOf(new ArrayList<>(((Map<String, Integer>) o).keySet())
                        .equals(KAYNAK)));
        sozluk.sor("bos anahtar kabul ediliyor mu", o -> {
            try { ((Map<String, Integer>) o).put(null, 0); return "evet"; }
            catch (RuntimeException e) { return "hayir"; }
        });
        System.out.println();
        Garanti.ozet();
    }
}
```

```
sozluk                          HashMap        LinkedHashMap  TreeMap        garantiyi veren
anahtar tekil mi                5              5              5              arayuz
anahtar gorunumu canli mi       true           true           true           arayuz
yok anahtarin yaniti ne         null           null           null           arayuz
gezinme sirasi ekleme sirasi mi false          true           false          gerceklestirim
bos anahtar kabul ediliyor mu   evet           evet           hayir          gerceklestirim

gozlem: 3 arayuz garantisi, 2 gerceklestirim davranisi
```

Üç satır arayüz sütununda topluyor. Bir anahtara ikinci kez yazmak boyutu büyütmüyor —
üçü de beş anahtarlı kalıyor, çünkü `Map` arayüzü anahtarın tekil olacağını doğrudan
tanımlıyor. `keySet()`'in döndürdüğü küme kabın kendisine **bağlı bir görünümdür**: sözlüğe
sonradan eklenen `"ekstra"` anahtarı, daha önce alınmış görünümde de beliriyor; görünüm bir
anlık kopya değil, kabın kendisine açılan bir penceredir. Üçüncü satır ise sözlüğün en
sık unutulan sözüdür: olmayan bir anahtarı aramak istisna fırlatmıyor, boş değer
döndürüyor. Üç ayrı gerçekleştirim de bu üç sözü aynen tutuyor; bir sözlük seçerken bu
üçü tartışmasız kabul edilebilir.

Bu üçüncü sözün kendi sınırı var ve onu görmek için tek bir satır yeterli. Sözlük boş
değeri **değer** olarak da kabul ediyorsa, `get`'in boş dönmesi iki ayrı durumu
ayırt edemez: anahtar hiç yok, ya da anahtar var ve karşılığı zaten boş değer.

```java
// Belirsizlik.java — get(anahtar) yokluk ile bos degeri ayirt edemiyor
import java.util.*;

public class Belirsizlik {
    public static void main(String[] args) {
        Map<String, Integer> m = new HashMap<>();
        m.put("alfa", null);
        System.out.println("get(alfa)         : " + m.get("alfa"));
        System.out.println("get(yok)          : " + m.get("yok"));
        System.out.println("containsKey(alfa) : " + m.containsKey("alfa"));
        System.out.println("containsKey(yok)  : " + m.containsKey("yok"));
    }
}
```

```
get(alfa)         : null
get(yok)          : null
containsKey(alfa) : true
containsKey(yok)  : false
```

İki `get` çağrısı da aynı yanıtı, boş değeri, veriyor; ama biri gerçekten yok olan bir
anahtarı, öteki var olan ve kasıtlı olarak boş değere bağlanmış bir anahtarı anlatıyor.
`get`'in tek başına yeterli olmadığı yer burası: yokluk sözünü tam okumak için `containsKey`
ayrı bir çağrı olarak gerekiyor. Arayüzün sözü "yok anahtar boş değer döndürür"dür; "boş
değer dönerse anahtar yoktur" değildir — ikisi aynı önerme değil.

## Karma ile Sıralının Ayrıldığı Yer

Tablonun alt iki satırı `HashMap`/`LinkedHashMap` ile `TreeMap`'i ayırıyor. Gezinme sırası
`HashMap`'te anahtarların karma değerine, `LinkedHashMap`'te ekleme sırasına, `TreeMap`'te
anahtarların doğal sırasına bakıyor — üçü de arayüzün değil, seçilen sınıfın kararı.
Boş anahtar kabulü de aynı çizgide ayrışıyor: `HashMap` ve `LinkedHashMap` boş anahtarı
tek bir özel yuvada tutuyor, `TreeMap` reddediyor, çünkü bir anahtarı yerleştirmek için
onu var olan anahtarlarla **karşılaştırması** gerekiyor ve boş değerin karşılaştırılacak
bir sırası yok.

Bu son gözlem üçüncü bir ayrımı işaret ediyor: `TreeMap`'in anahtardan istediği şey
`HashMap`'in istediğinden farklı. `HashMap` bir anahtarı yalnız `equals` ve `hashCode` ile
tanır; `TreeMap` bir sıra bekler. Bu, arayüzün kendisinde görünmeyen bir farktır — ikisi de
`Map<K, V>` gerçekleştirir, ikisinin de imzasında `K` üzerine hiçbir ek kısıt yazmaz.
Fark yalnız çalışma zamanında, anahtar ilk kez yerleştirilmeye çalışıldığında ortaya
çıkar. Anahtar sınıfı sıralanabilir değilse ne olur?

- **KO13** — Aynı anahtar sınıfı `equals` ve `hashCode`'u eksiksiz yazar ama hiçbir
  sıralama bildirmez (`Comparable` gerçekleştirmez). Üç sözlüğe de aynı tek anahtarla tek
  bir `put` çağrısı yapılır.

```java
// Zorunluluk.java — anahtarin siralanabilir olmasi hangi gerceklestirimde zorunlu
import java.util.*;

public class Zorunluluk {
    static class KarsilastirilamazAnahtar {
        final String ad;
        KarsilastirilamazAnahtar(String ad) { this.ad = ad; }
        @Override public boolean equals(Object o) {
            return o instanceof KarsilastirilamazAnahtar a && a.ad.equals(ad);
        }
        @Override public int hashCode() { return ad.hashCode(); }
    }

    static String dene(Runnable islem) {
        try { islem.run(); return "basarili"; }
        catch (RuntimeException e) { return e.getClass().getSimpleName(); }
    }

    public static void main(String[] args) {
        Map<KarsilastirilamazAnahtar, Integer> hash = new HashMap<>();
        Map<KarsilastirilamazAnahtar, Integer> baglantili = new LinkedHashMap<>();
        Map<KarsilastirilamazAnahtar, Integer> siralanan = new TreeMap<>();

        System.out.println("HashMap.put       : " + dene(() -> hash.put(new KarsilastirilamazAnahtar("a"), 1)));
        System.out.println("LinkedHashMap.put : " + dene(() -> baglantili.put(new KarsilastirilamazAnahtar("a"), 1)));
        System.out.println("TreeMap.put       : " + dene(() -> siralanan.put(new KarsilastirilamazAnahtar("a"), 1)));
    }
}
```

```
HashMap.put       : basarili
LinkedHashMap.put : basarili
TreeMap.put       : ClassCastException
```

`equals` ve `hashCode` tam yazılmış, hiçbir eksik yok — ve yine de üçüncü satır çöküyor.
`HashMap` ile `LinkedHashMap` anahtarı yuvasına yerleştirmek için yalnız karma değerini ve
eşitliğini bilmesi gerekiyor; `TreeMap` ise ilk `put` çağrısında bile anahtarı ağaçtaki
konumuna yerleştirmek için onu bir şeyle **karşılaştırmak** zorunda ve elinde ne bir
`Comparable` ne bir `Comparator` var. Kusur derleme zamanında görünmüyor — `Map<K, V>`
tanımı `K` üzerine hiçbir sıralama kısıtı koymuyor — ve çalışma zamanında, ilk yazmada,
adıyla düşüyor.

## Çağıranın Kuralı: equals Yazıp hashCode Yazmamak

Yukarıdaki iki bölüm arayüzün ve gerçekleştirimin sözlerini ayırdı. Geriye üçüncü kaynak
kalıyor: çağıranın kendi yazdığı anahtar sınıfının uyması gereken kural. `Object` sınıfı
bir sözleşme tanımlar — iki nesne `equals` ile eşit sayılıyorsa `hashCode`'ları da eşit
olmalıdır — ama derleyici bu sözleşmeyi hiçbir yerde denetlemez. `equals` yazılıp
`hashCode` yazılmadığında ne olur?

- **KO14** — Anahtar sınıfı `equals`'ı doğru yazar (adı karşılaştırır) ve `hashCode`'u
  **hiç yeniden tanımlamaz**; miras aldığı `Object.hashCode()` çalışma zamanı kimliğine
  bakar. İki ayrı `new` çağrısı aynı adı taşısa da iki ayrı karma değeri üretir.

```java
// EslesmeyenAnahtar.java — equals yazilmis, hashCode yazilmamis bir anahtarla arama
import java.util.*;

public class EslesmeyenAnahtar {
    static class Anahtar {
        final String ad;
        Anahtar(String ad) { this.ad = ad; }
        @Override public boolean equals(Object o) {
            return o instanceof Anahtar a && a.ad.equals(ad);
        }
        // hashCode kasitli olarak yazilmadi
    }

    public static void main(String[] args) {
        Map<Anahtar, Integer> depo = new HashMap<>();
        depo.put(new Anahtar("alfa"), 9);
        Integer bulunan = depo.get(new Anahtar("alfa"));
        String sonuc = String.valueOf(bulunan);
        System.out.printf("%-30s %-9s bulunan=%-6s beklenen=9%n", "hashCode yazilmamis anahtar",
                sonuc.equals("9") ? "basarili" : "sessiz", sonuc);
    }
}
```

```
hashCode yazilmamis anahtar    sessiz    bulunan=null   beklenen=9
```

Yazan da arayan da aynı adı, `"alfa"`'yı, kullanıyor; `equals` ikisini eşit sayardı. Ama
`HashMap` anahtarı önce hangi yuvaya bakacağına karar vermek için `hashCode`'a başvuruyor
ve iki ayrı `new Anahtar("alfa")` nesnesi, `Object`'ten miras kalan varsayılan `hashCode`
yüzünden, neredeyse kesinlikle iki ayrı yuvaya düşüyor. Arama, değerin durduğu yuvaya hiç
uğramıyor ve `equals` çağrılma fırsatı bile bulamıyor. Sonuç **sessiz**: hiçbir istisna
fırlamıyor, `get` yalnızca boş değer döndürüyor — sanki anahtar hiç eklenmemiş gibi.
Kusurun görüneceği yer yazma satırı değil, aylar sonra yazılmış bir arama satırı olabilir.

Bu üç sınıfa ayrılan sonuç — `istisna`, `sessiz`, `etkisiz` — kursun kendi
sınıflamasıdır ve burada ilk kez somut bir örnekle karşılanıyor. `hashCode` eksikliği
`istisna` sınıfına girmiyor: hiçbir satır çökmüyor, program baştan sona çalışıyor. Aynı
sözlük binlerce anahtarla dolu olsaydı, kusur yine görünmezdi; yalnızca yanlış bir sayıda
öge "kayıp" görünürdü ve bu kaybın nedeni günler sonra, kaynağa hiç dokunmadan bulunması
gereken bir hata olurdu.

## Sınırlayıcı Ölçüm: Sıralı Sözlükte Aynı Anahtar Bulunuyor

Bir önceki ölçüm "bu anahtar sınıfı bozuk" sonucuna götürüyor gibi görünüyor. Sınırlayıcı
ölçüm bunu düzeltiyor: aynı sınıf, aynı eksik `hashCode`, tek fark gerçekleştirim seçimi.

- **KO15** — Anahtar sınıfı yukarıdakiyle **birebir aynıdır**; `hashCode` yine yazılmamıştır.
  Tek değişen, sözlüğün `TreeMap` olması ve bir `Comparator` ile kurulmasıdır.

```java
// SiraliDeBulunuyor.java — ayni kusurlu anahtar siralamaya dayanan sozlukte bulunuyor
import java.util.*;

public class SiraliDeBulunuyor {
    static class Anahtar {
        final String ad;
        Anahtar(String ad) { this.ad = ad; }
        @Override public boolean equals(Object o) {
            return o instanceof Anahtar a && a.ad.equals(ad);
        }
        // hashCode kasitli olarak yazilmadi
    }

    public static void main(String[] args) {
        Map<Anahtar, Integer> siralanan = new TreeMap<>(Comparator.comparing(a -> a.ad));
        siralanan.put(new Anahtar("alfa"), 9);
        Integer bulunan = siralanan.get(new Anahtar("alfa"));
        System.out.println("TreeMap.get, ayni kusurlu anahtar : " + bulunan);
    }
}
```

```
TreeMap.get, ayni kusurlu anahtar : 9
```

Aynı sınıf, aynı eksik yöntem, ve arama bu kez **buluyor**. Fark anahtarda değil, anahtarı
okuyan gerçekleştirimde: `TreeMap` bir konum bulmak için hiçbir zaman `hashCode`'a
bakmıyor, yalnız verilen `Comparator`'ı çağırıyor ve o da yalnız `ad` alanını okuyor. Bu,
ilk ölçümün yanlış okunmasını engelliyor. Kusurlu olan anahtar sınıfının kendisi değildi;
kusur, bir sınıfın verdiği sözle (yalnız `equals`) seçilen gerçekleştirimin beklediği
sözün (`hashCode` ve `equals` birlikte) **eşleşmemesiydi**. Aynı anahtar, beklentisi farklı
bir gerçekleştirimin elinde, kusursuz çalışıyor.

Bu iki ölçüm yan yana konduğunda kursun sorusu tam olarak buraya geliyor. Bir kusuru
tarif ederken "bu sınıf bozuk" demek çoğu zaman yanlış sorudur; doğru soru "bu sınıf hangi
sözü veriyor ve onu okuyan taraf hangi sözü bekliyor" sorusudur. `HashMap` `hashCode`
bekler ve anahtar onu vermez — kusur orada. `TreeMap` yalnız bir sıra bekler ve anahtar,
dolaylı yoldan bir `Comparator` üzerinden, onu verir — kusur orada yok. Anahtar sınıfı
hiç değişmedi; değişen, anahtarın hangi sözleşmeye karşı sınandığıydı.

## Özet

- Sözlüğün üç sözü — anahtarın tekilliği, anahtar görünümünün canlılığı, yok anahtarın
  boş değer vermesi — üç gerçekleştirimde de aynı çıktı ve arayüze aitti.
- Gezinme sırası ve boş anahtar kabulü karma ile sıralı gerçekleştirim arasında ayrıştı;
  ayrımın kökeninde `TreeMap`'in anahtarı **karşılaştırma** zorunluluğu var.
- Sıralanamayan bir anahtar `HashMap`'te sorunsuz çalışırken `TreeMap`'te ilk yazmada
  `ClassCastException` ile düşüyor — kusur derlemede değil, çalışma zamanında görünüyor.
- Çağıranın kuralı: `equals` yazılıp `hashCode` yazılmadığında arama hiçbir istisna
  vermeden boş değer döndürüyor; kusur yazma satırında değil, arama satırında ortaya
  çıkıyor.
- Sınırlayıcı ölçüm: aynı eksik anahtar sınıfı sıralı bir sözlükte sorunsuz bulunuyor,
  çünkü orada aranan söz `compareTo`/`Comparator`'dır. Kusur anahtarda değil, anahtarla
  gerçekleştirimin beklentisinin eşleşmemesindeydi.

## Sonraki Adım

Bu derste bir sözlüğün anahtarı bulup bulamayacağı ölçüldü; peki kap **gezinirken**
değişirse ne olur? Bir sözlüğün ya da kümenin üzerinde `for` ile ilerlerken aynı kaba yeni
bir öge eklemek ya da bir ögeyi silmek, üç ailenin de karşılaştığı ortak bir durumdur.
Sıradaki ders bu durumu ölçer: bazı gerçekleştirimler hemen ve adıyla bir istisna fırlatır,
bazıları hiçbir şey söylemeden yanlış bir gezinti tamamlar — ve hangisinin olacağına yine
arayüz değil, gerçekleştirim karar verir. Bu derste görülen "kusur derlenir, çalışır, ve
sessizce yanlış sonuç verir" örüntüsü orada bir kez daha, farklı bir sebeple karşımıza
çıkacak.
