---
title: Sözlükler
source: 'https://academia.sh/tr/kurslar/go-temelleri/sozlukler'
course: 'Go Temelleri'
language: tr
updated: '2026-08-23T14:25:00+00:00'
license: 'CC BY-SA 4.0'
---

# Sözlükler

Bir sözlükten okumak dört ayrı biçimde dört ayrı sınıfa düşüyor: var olan anahtar değerini veriyor, olmayan anahtar sıfır değerle birlikte bir yokluk yanıtı veriyor, nil bir sözlükten okumak tanımlı ama ona yazmak panikliyor, silinen bir anahtar yeniden yokmuş gibi davranıyor. Gezinme sırası bilerek rastgeledir ve hiçbir yerde doğrudan yazdırılmaz. Sınırlayıcı ölçüm iki yanıtlı erişimin neden gerekli olduğunu gösteriyor: değeri sıfır olan bir kayıt ile hiç var olmayan bir kayıt, tek yanıtlı erişimde ayırt edilemiyor.

Önceki ders dilimlerde paylaşımın ne zaman sürüp ne zaman kesileceğinin, kaynak koddan
değil kapasiteden okunduğunu ölçtü ve bunu ayırmayı ikinci bir soruya bıraktı: anahtar-değer
eşleşmesinde olmayan bir anahtara erişildiğinde ne olur, ve bu yanıt tek bir değerle mi
geliyor, iki değerle mi? Bu ders o soruyla başlıyor.

Go'da anahtar-değer eşleşmesinin adı **sözlük**tür (`map`). Dilim gibi sözlük de bir
başlıktır — kendi verisini değil, çalışma zamanının yönettiği bir karma tabloya giden bir
göstericiyi taşır. Ama sözlüğün yazılmayan bir anahtara verdiği yanıt, dilimin yazılmayan
bir sınıra verdiği yanıttan daha katmanlı: dört ayrı erişim biçimi, dört ayrı sınıfa
düşüyor.

Bu katmanlılığın kaynağı, dilimin yalnızca **konum** üzerinden erişilmesine karşın
sözlüğün **anahtar** üzerinden erişilmesidir. Bir dilimde geçersiz bir konuma erişmek
her zaman aynı şeyi yapar — sınırların dışına taşan bir indeks panikler. Bir sözlükte
"anahtar yok" durumu panik değildir, çünkü sözlük olmayan bir anahtarı aramanın normal bir
sonuç olduğunu, tükenmiş bir sınır değil sıradan bir sorgu olduğunu varsayıyor. Panik yalnız
sözlüğün kendisi hiç var olmadığında (yazma denemesinde) geliyor.

## Dört Erişim Biçimi

```go
// sozluk.go — sozlukte dort erisim bicimi, dort ayri sinif
package main

import "fmt"

func panikliyorMu(f func()) (p bool) {
	defer func() {
		if recover() != nil {
			p = true
		}
	}()
	f()
	return false
}

func main() {
	sayac := map[string]int{"kalem": 3, "defter": 7, "silgi": 1}

	deger, varMi := sayac["kalem"]
	fmt.Println("var olan anahtar    : deger=", deger, " varMi=", varMi)

	deger, varMi = sayac["mudur"]
	fmt.Println("olmayan anahtar     : deger=", deger, " varMi=", varMi)

	var bosSayac map[string]int
	fmt.Println("nil sozlukten okuma : deger=", bosSayac["herhangi"])

	yazmaPanikledi := panikliyorMu(func() { bosSayac["yeni"] = 1 })
	fmt.Println("nil sozluge yazma   : panik=", yazmaPanikledi)

	delete(sayac, "silgi")
	degerSil, varMiSil := sayac["silgi"]
	fmt.Println("silinen anahtar     : deger=", degerSil, " varMi=", varMiSil)
}
```

```
var olan anahtar    : deger= 3  varMi= true
olmayan anahtar     : deger= 0  varMi= false
nil sozlukten okuma : deger= 0
nil sozluge yazma   : panik= true
silinen anahtar     : deger= 0  varMi= false
```

Beş satır dört sınıfı ayırıyor. Kaynaktaki `deger, varMi := sayac["kalem"]` yazımının adı
**iki yanıtlı erişim**dir: sözlükten okumak, istenirse tek bir değer yerine değer ve
varlık bilgisini birlikte döndürür. Var olan bir anahtardan okumak beklendiği gibi çalışıyor.
Olmayan bir anahtar (`sayac` içinde hiç bulunmayan `"mudur"`) hata vermiyor, `int`in sıfır
değeriyle birlikte bir yokluk yanıtı veriyor — bu ikinci yanıt aşağıda ayrı bir bölümün
konusu. Hiç ilk değeri verilmemiş bir sözlükten — `bosSayak`, `nil` durumunda — okumak da
çalışıyor ve yine sıfır değer dönüyor; Sıfır Değerler dersinin bağlayıcı 8/4/3 dağılımında
bu satır **tanımlı** sınıfına giren sekiz durumdan biriydi. Ama aynı `nil` sözlüğe **yazmak**
farklı bir sınıfa düşüyor: dil burada bir sıfır değer koymuyor, çalışma zamanı panikliyor —
aynı dağılımın **açıkta** kalan üç durumundan biri. Son satır silinen bir anahtarın, hiç
var olmamış bir anahtardan davranışça ayırt edilemediğini gösteriyor: `delete` sonrası
`sayac["silgi"]` de tıpkı `sayac["mudur"]` gibi sıfır değer ve `false` veriyor.

Dört erişim biçiminin üçü aynı kalıba uyuyor — okumak her zaman güvenli, yazmak `nil`
sözlükte güvenli değil. Bu asimetri rastgele değil: bir sözlükten okumak yalnızca karma
tabloda arama yapar ve bulamazsa sıfır değer döndürür, ama yazmak karma tabloya bir girdi
eklemeyi gerektirir ve `nil` bir sözlüğün henüz **hiçbir** karma tablosu yoktur — eklenecek
yer, dilimin `nil` durumundaki gibi sıfır uzunluklu bir alt dizi değil, tümüyle **yok**. Bu
asimetri `delete`e kadar uzanıyor: `nil` bir sözlükten var olmayan bir anahtarı silmeye
çalışmak da panik vermiyor, çünkü `delete` de yazma gibi değil okuma gibi davranıyor —
silinecek bir şey bulamazsa sessizce hiçbir şey yapmıyor.

## Anahtar Tipi Karşılaştırılabilir Olmak Zorundadır

Diziler ve Dilimler dersi bir dizinin `==` ile karşılaştırılabildiğini, bir dilimin
karşılaştırılamadığını ölçmüştü ve bunun bir sözlük anahtarı olarak kullanılabilirlikle
ilgili olduğunu bir cümleyle bırakmıştı. Sözlüğün kendi arama düzeneği bu bağlantıyı
zorunlu kılıyor: bir anahtarı bulmak için sözlük onu var olan anahtarlarla **eşitlik**
üzerinden karşılaştırıyor, bu yüzden anahtar tipi karşılaştırılabilir olmak zorunda.

**BT9.** — Bu kısıt bir derleyici çağrısıyla koşturuluyor; yalnızca "derlendi" ya da
"derlenmedi" sınıfı basılıyor.

```go
// anahtar.go — sozluk anahtari karsilastirilabilir olmak zorunda
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
)

func gecerMi(kaynak string) bool {
	dizin, err := os.MkdirTemp("", "sinama")
	if err != nil {
		return false
	}
	defer os.RemoveAll(dizin)
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
	os.WriteFile(filepath.Join(dizin, "kod.go"), []byte(kaynak), 0o644)
	cmd := exec.Command("go", "build", "./...")
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	return cmd.Run() == nil
}

func main() {
	diziAnahtar := `package sinama
	func F() {
		m := map[[2]int]string{}
		m[[2]int{0, 0}] = "sol-ust"
	}`
	dilimAnahtar := `package sinama
	func F() {
		m := map[[]int]string{}
		_ = m
	}`
	fmt.Println("dizi anahtarli sozluk  :", gecerMi(diziAnahtar))
	fmt.Println("dilim anahtarli sozluk :", gecerMi(dilimAnahtar))
}
```

```
dizi anahtarli sozluk  : true
dilim anahtarli sozluk : false
```

Dizi anahtarlı sözlük derleniyor, çünkü dizinin karşılaştırılabilirliği önceki derste
kuruldu. Dilim anahtarlı sözlük derlenmiyor — dilim karşılaştırılamadığı için sözlüğün
kendisi bile **tanımlanamıyor**, tek bir girdi eklenmeden önce. Bu, iki bileşik tipin aynı
özelliğin (karşılaştırılabilirlik) iki ayrı yerde nasıl sonuç doğurduğunu gösteriyor: biri
`==` işlecinde, öteki bir sözlüğün anahtar tipi olarak kabul edilip edilmemesinde.

## Fonksiyona Geçerken: Sözlük Dilimden Farklı Davranır

Önceki ders bir işlevin aldığı dilime `append` ile öğe eklemesinin, sonucu geri
döndürmediği sürece çağırana hiç yansımadığını ölçmüştü. Sözlükte aynı tuzak yok.

```go
// gecis.go — sozluge yazmak, sonucu dondurmeden de cagirana yansir
package main

import "fmt"

func ekleGuvenli(s map[string]int, anahtar string, deger int) {
	s[anahtar] = deger
}

func main() {
	sayac := map[string]int{"kalem": 3}
	ekleGuvenli(sayac, "silgi", 5)
	fmt.Println("cagirandaki sozluk:", sayac["silgi"])
}
```

```
cagirandaki sozluk: 5
```

`ekleGuvenli` hiçbir şey döndürmüyor, ama `sayac["silgi"]` çağırandan bakıldığında `5`.
Fark, dilimin `append`iyle sözlüğün yazma işleminin farklı şeyler yapmasından geliyor:
`append` kapasiteyi aştığında **yeni** bir alt dizi ayırıp başlığı günceller — bu güncelleme
yalnızca yerel kopyada olur. Sözlüğe yazmak ise var olan karma tabloyu **yerinde**
değiştirir; başlığın kendisi hiç değişmez, yalnızca tablonun içeriği değişir. Parametre
olarak geçen sözlük değişkeni çağırandakiyle aynı tabloyu gösterdiği için, tabloya yapılan
her değişiklik çağırana görünür. Sözlüğün dilimden ayrıldığı yer tam olarak burası: dilim
büyürken bazen alt dizisini değiştirmek zorunda kalıyor, sözlük ise büyürken kendi
tablosunu asla değiştirmiyor — yalnızca genişletiyor.

## Kapasite İpucu, Kapasite Sınırı Değildir

`make` ile bir sözlük kurulurken verilebilen ikinci argüman, dilimdeki kapasiteyle aynı
sözü taşımıyor. Dilimde kapasite bir sınırdı — aşıldığında `append` yeni bir alt dizi
ayırırdı. Sözlükte aynı sayı yalnızca bir **ipucudur**; karma tablonun başlangıç
büyüklüğünü belirler ama sözlüğün büyümesini hiçbir şekilde sınırlamaz.

```go
// ipucu.go — sozluk kapasitesi bir ipuçudur, sinir degildir
package main

import "fmt"

func main() {
	kucuk := make(map[string]int, 2) // yalnizca baslangic ipucu
	for i, ad := range []string{"a", "b", "c", "d", "e", "f"} {
		kucuk[ad] = i
	}
	fmt.Println("baslangic ipucu=2, eklenen anahtar sayisi:", len(kucuk))
}
```

```
baslangic ipucu=2, eklenen anahtar sayisi: 6
```

`kucuk` yalnızca iki anahtar için ayrılmış bir ipucuyla kuruldu ama altı anahtar sorunsuz
eklendi. `make([]int, 0, 2)` yazımıyla kurulan bir dilime üçüncü öğeyi eklemek `append`i
yeni bir alt dizi ayırmaya zorlardı ve bu, Dilim İşlemleri dersinde ölçülen paylaşım
kesilmesine yol açardı; sözlükte böyle bir eşiği geçme anı yok, çünkü ikinci argüman
kapasiteyi **taahhüt etmiyor**, yalnızca çalışma zamanına bir tahmin veriyor. Sözlüğün
kendi iç tablosu gerektiğinde büyür, ama bu büyüme dilimin `append`i gibi çağırana
görünen bir başlık değişikliği doğurmaz — çünkü sözlüğün başlığı zaten çağıranla paylaşılan
tek başlıktır, yeniden atanacak ikinci bir kopyası yoktur.

## Gezinme Sırası: Tanımlı Bir Belirsizlik

Bir sözlük üzerinde `range` ile gezinmenin sırası bilerek rastgeledir; aynı sözlük aynı
program içinde bile iki ayrı `range` çağrısında farklı sırayla dolaşılabilir. Bu bir
gerçekleştirim kusuru değil, **tanımlı bir belirsizliktir**: dil, gezinme sırasının hiçbir
koda bağımlı hâle gelmemesi için sırayı bilinçli olarak karıştırıyor. Bu yüzden kursun
hiçbir dersinde bir sözlük doğrudan yazdırılmıyor; bunun yerine anahtarlar sıralanıyor.

```go
// gezinme.go — sozluk dogrudan yazdirilmaz, anahtarlar siralanir
package main

import (
	"fmt"
	"sort"
)

func main() {
	sayac := map[string]int{"kalem": 3, "defter": 7, "silgi": 1}
	anahtarlar := make([]string, 0, len(sayac))
	for a := range sayac {
		anahtarlar = append(anahtarlar, a)
	}
	sort.Strings(anahtarlar)
	for _, a := range anahtarlar {
		fmt.Println(" ", a, "=", sayac[a])
	}
}
```

```
  defter = 7
  kalem = 3
  silgi = 1
```

Burada basılan sıra alfabetiktir, çünkü onu üreten `sort.Strings` çağrısıdır — sözlüğün
kendi gezinme sırası değil. `for a := range sayac` döngüsü yalnızca anahtarları bir dilime
topluyor; hangi sırayla topladığı bu ölçüm için önemsiz, çünkü `sort.Strings` sonucu her
koşumda aynı sıraya getiriyor.

Bilerek rastgele tutulan sıranın amacı, kodun sıraya **bağımlı hâle gelmesini** baştan
engellemektir. Sıra tutarlı olsaydı — örneğin her zaman ekleme sırasını izleseydi — bu
tutarlılığa güvenen kod zamanla birikirdi: bir döngü çıktısını ekleme sırasına göre
biçimlendiren, ya da iki sözlüğü aynı sırada gezip karşılaştıran kod. Böyle bir kod, karma
tablonun iç yapısı değiştiği anda (örneğin bir anahtar silinip yeniden eklendiğinde)
sessizce bozulurdu. Sırayı baştan rastgele tutmak, bu bağımlılığın hiç oluşmamasını
garantiliyor — programcı sıraya güvenemez, çünkü güvenilecek sabit bir sıra hiç yok.

Bir sözlüğü `fmt.Println(sayac)` ile doğrudan yazdırmak da teknik olarak çalışır ve hatta
biçimlendirme paketi çıktıyı anahtara göre sıralı üretebilir — ama bu, dilin garantisi
değil, biçimlendirme paketinin kendi seçimidir; `range` ile doğrudan gezinildiğinde aynı
garanti yoktur. Ölçüm bu farka güvenmiyor ve sıralamayı kendi elleriyle yapıyor; kursun
hiçbir dersi bir sözlüğü `range` çıktısına güvenerek yazdırmıyor.

**BT10.** — Sıralama üç anahtarlık küçük bir kümede yapılıyor; ölçümün amacı sıralamanın
kendisini göstermek değil, sözlüğün **kendi** sırasına hiç dayanılmadığını göstermek. Aynı
sonuç yüzlerce anahtarla da alınır, yalnızca çıktısı daha uzun olurdu.

## Sınırlayıcı Ölçüm: Sıfır Değer ile Yokluk Tek Yanıtla Ayırt Edilemez

İki yanıtlı erişimin neden var olduğu, tek yanıtlı erişimin nerede yetersiz kaldığı
görülmeden tam anlaşılmıyor. Bir anahtarın değeri, tipin sıfır değeriyle **aynı** olduğunda
sözlük iki farklı durumu artık ayırt edemiyor: anahtar var ve değeri sıfır, ya da anahtar
hiç yok.

```go
// belirsizlik.go — sifir degerli kayit ile yokluk, tek yanitla ayirt edilemez
package main

import "fmt"

func main() {
	sayac := map[string]int{"kalem": 3}
	sayac["yeni-urun"] = 0 // kayit var, degeri kasten sifir
	tekYanit := sayac["yeni-urun"]
	tekYanitYok := sayac["hic-eklenmemis"]
	fmt.Println("var, degeri sifir olan   : tek yanit=", tekYanit)
	fmt.Println("hic eklenmemis           : tek yanit=", tekYanitYok)

	_, varMi1 := sayac["yeni-urun"]
	_, varMi2 := sayac["hic-eklenmemis"]
	fmt.Println("iki yanitla ayrim: yeni-urun varMi=", varMi1, " hic-eklenmemis varMi=", varMi2)
}
```

```
var, degeri sifir olan   : tek yanit= 0
hic eklenmemis           : tek yanit= 0
iki yanitla ayrim: yeni-urun varMi= true  hic-eklenmemis varMi= false
```

İlk iki satır birebir aynı: `0`. Tek yanıtlı erişimden bakıldığında `"yeni-urun"` ile
`"hic-eklenmemis"` arasında hiçbir fark yok — ikisi de sıfır veriyor. Üçüncü satır bu
körlüğü gideriyor: `varMi1` `true`, `varMi2` `false`. Fark, değerin kendisinde değil,
sözlüğün o anahtarı **hiç tutup tutmadığında**.

Bu sınırlama pratikte gerçek sonuçlar doğuruyor. Bir sayaç sözlüğünde "hiç görülmedi" ile
"görüldü ama sıfır kez arttı" arasında fark varsa, ya da bir yapılandırma haritasında
"varsayılan alınmış" ile "açıkça sıfıra ayarlanmış" arasında fark varsa, tek yanıtlı erişim
bu ayrımı hiç göremiyor. İki yanıtlı erişim burada zorunlu bir yazım değil, **tek** doğru
yazım hâline geliyor — "tanımlı" olmak, tek başına, "yeterli" olmak anlamına gelmiyor.

Bu sınırlama, dersin baştaki dört sınıfını da yeniden okumayı gerektiriyor. "Olmayan
anahtar" ile "silinen anahtar" bölümündeki iki satır tek yanıtla bakıldığında aynı `0`'ı
verdi; iki yanıtlı erişim eklenmeden önce bu ikisinin — ve değeri gerçekten sıfır olan
üçüncü bir anahtarın — birbirinden ayrılamayacağı görülmüyordu. Sözlüğün verdiği tek
yanıt, "bir değer var" ile "değer yok, yerine sıfır kondu" arasındaki farkı taşımıyor;
taşıyan, ikinci dönüş değeridir.

## Özet

- Bir sözlükten dört erişim biçimi dört ayrı sınıfa düşüyor: var olan anahtar değerini
  veriyor, olmayan anahtar sıfır değerle bir yokluk yanıtı veriyor, `nil` sözlükten okumak
  tanımlı ama ona yazmak panikliyor, silinen bir anahtar yeniden yokmuş gibi davranıyor.
- Sözlüğe yazmak var olan tabloyu yerinde değiştirir; bir işleve geçirilen sözlüğe yapılan
  yazma, sonucu geri döndürmeden de çağırana yansır — dilimin `append` davranışından farklı.
- Sözlüğün gezinme sırası bilerek rastgeledir ve hiçbir derste doğrudan yazdırılmaz;
  anahtarlar `sort.Strings` ile sıralanıp öyle basılır.
- İki yanıtlı erişim, sıfır değerli bir kaydı yokluktan ayırmanın tek yoludur; tek yanıtlı
  erişim ikisini de aynı sıfır değerle döndürür.

## Sonraki Adım

Sözlüğün anahtarı, tek bir tam sayı ya da dizgi gibi bölünmez bir değerdi; sayaç örneğindeki
gibi tek bir bilgiyi taşıyordu. Sıradaki tip birden çok alanı bir arada tutuyor ve kendi
alanlarının her birine ayrı ayrı erişilebiliyor — sözlüğün tek bir anahtarla eriştiği yerde,
bu yeni tip adlandırılmış birden çok alanla erişiyor. Yapı da yazılmayan bir şeyle başlıyor:
alanlarına değer verilmezse ne olur? Ama sözlükten farklı olarak alanların **sırası** da bir
anlam taşıyor, ve bu sıraya güvenmenin kendi kırılganlığı var — sözlüğün anahtar adıyla
eriştiği yerde bu yeni tip, konumla erişmenin de bir seçenek olduğu bir alan taşıyor.
