Kapak Fotoğrafı John Jackson

Kod örnekleri: Microsoft Learn – Use Maps

Bir önceki yazıda Go’da Slice’ları incelemiştik. Bu yazıda benzer ama biraz daha gelişmiş bir seçenek olan Go Map’leri inceleyeceğiz.

Go Map’leri bazı programlama dillerinde bulunan hash-table kavramına denk bir veri tipi. Array ve Slice’lardan en büyük farkları ise index anahtarı olarak herhangi bir veri tipini kullanmaya olanak vermeleri.

Her ne kadar Go anahtar olarak kullanılabilecek veri tipi için bir sınırlama getirmese de, Go derleyicisinin anahtarları birbirinden ayırabilmesi için kullanılacak anahtarın karşılaştırılabilir (comparable) olması gerekiyor.

Go Map veri tipi nerede / ne zaman kullanılmalı?

Map tipi slice ve array’e göre çok daha kullanışlı olsa da kaynak açısından biraz daha masraflı. Fakat ileri seviyede optimize bir kod oluşturmaya çalışmıyorsanız görmezden gelinebilecek düzeyde. Slice ile halledilebilecek noktalarda gereksiz yere map kullanmamalıyız, fakat map’in gerek işlev, gerekse anlaşılırlık bakımından işimizi kolaylaştırdığı yerlerde tereddüt etmeden kullanabiliriz.

Go’da Map Tanımlama

Diğer veri tiplerinde olduğu gibi map veri tipinde bir değişken oluşturmak için de bir kaç farklı yöntem kullanabiliriz.

Örneğin anahtarları string, sakladığı değerler int olan bir map tanımlamak için aşağıdaki kodu yazabiliriz;

var studentsAge map[string]int;
studentsAge = make(map[string]int)

Dilerseniz başlangıç değerlerini atayarak da bir map oluşturabilirsiniz

studentsAge := map[string]int{
  "john": 32,
  "bob":  31,
}

Go Maplerine Eleman Ekleme

Slice’dan farklı olarak Map’lere eleman eklemek için özel bir fonksiyona ihtiyaç duymuyoruz. Kullanım yöntemimiz daha çok Javascript ya da PHP’deki “associative array” mantığına benziyor. Yukarıda kullandığımız ilk kod üzerinden devam ederek oluşturduğumuz map değişkenine yeni anahtar/değişken çiftleri ekleyelim.

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    fmt.Println(studentsAge)
}

Bu şekilde studentAge değişkenimizi tanımladıktan sonra yeni bir anahtar/değişken çifti ekleyebiliyoruz. Bu işlemi yaptığımız studentsAge map değişkeninin boş değerini map fonksiyonu ile oluşturduğumuza dikkat edin. Eğer bu değişkeni oluşturmadan, veya sadece tanımını yaparak ekleme yapmaya çalışsaydık hatayla karşılaşırdık.

package main

import "fmt"

func main() {
    var studentsAge map[string]int
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    fmt.Println(studentsAge)
}

Yukarıdaki kod’da studentsAge değişkeninin string anahtarları olan ve integer değerler sağlayan map tipinde bir değişken olduğunu tanımladık fakat aslında bu değişkeni oluşturmadık (initialize edilmedi) Bu şekilde çalıştırırsak aşağıdaki hatayı alırız.

panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
        /Users/johndoe/go/src/helloworld/main.go:7 +0x4f
exit status 2

Bu tarz bir hatayla karşılaşmamak için map tanımını yaparken make fonksiyonu ile (nil) bir map oluşturmaya dikkat edin. ( Eğer sıfırdan oluşturmak yerine başka bir kullanım planlamıyorsanız ) Boş bir map’de yapacağınız işlemler hataya neden olmaz, ama eğer map oluşturulmadıysa hata alırsınız.

Go Map’lerine Erişim

Go map’lerindeki bir elemana erişim, değer atama işlemindeki kadar kolay. Map değişkeninizin adının m olduğunu varsayarak m[anahtar] şeklinde bir kullanımla o anahtara ait değere ulaşabilirsiniz.

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["selim"] = 32
    studentsAge["fatma"] = 31
    fmt.Println("Selim ", studentsAge["selim"], " yaşında")
}

Eğer var olmayan bir abahtara ait değeri almak isterseniz Go bir hata döndürmez, bunun yerine map’in sakladığı veri tipi için varsayılan değer neyse onu döndürür. Örneğin map[string]int şeklinde tanımlanan bir map integer değerler sakladığı için, var olmayan bir anahtarın değeri çağrıldığında int tipi için varsayılan değer olan 0 değeri döndürülür.

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["selim"] = 32
    studentsAge["fatma"] = 31
    fmt.Println("Mahmut ", studentsAge["mahmut"], " yaşında")
}

Yukarıdaki kodu çalıştırarak map’de olmayan mahmut anahtarının değerini istersek, map’in sakladığı int tipinin varsayılan değeri olan sıfır döndürülecektir. Ekrana basılan çıktı şöyle olur;

Mahmut 0 yaşında

Go Map’lerinde anahtar mevcut mu nasıl anlarız?

Go gibi hatalar konusunda hassas bir dilin map’de var olmayan bir anahtar değeri için hata vermemesi güzel diye düşünebilirsiniz. Peki ya o zaman bir anahtar o map’de tanımlı mı değil mi nasıl anlayacağız? Go aslında bunu bize söylüyor. Map anahtarına aşağıdaki şekilde ulaşırsanız geriye iki değer dönecektir, birincisi o anahtara ait değer ( eğer anahtar yoksa varsayılan değeri ), diğeri ise o anahtarın map’de tanımlı olup olmadığını gösteren boolean tipinde bir değer.

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["selim"] = 32
    studentsAge["fatma"] = 31

    age, exist := studentsAge["mahmut"]
    if exist {
        fmt.Println("Mahmut", age, " yaşında")
    } else {
        fmt.Println("Mahmut adında bir kişi yok!")
    }
}

Yukarıdaki kodu çalıştırdığımızda aşağıdaki çıktıyı alırız

Mahmut adında bir kişi yok!

Go Map’lerinden eleman silme

Go maplerinden bir eleman silmek için bir başka dahili Go fonksiyonu olan delete() fonksiyonunu kullanabilirsiniz. Aşağıdaki örnek bu fonksiyonun nasıl kullanıldığını gösteriyor;

package main

import "fmt"

func main() {
    studentsAge := make(map[string]int)
    studentsAge["selim"] = 32
    studentsAge["fatma"] = 31
    delete(studentsAge, "selim")
    fmt.Println(studentsAge)
}

Bu kodu çalıştırırsak tanımladığımız selim anahtarını silmiş olur ve aşağıdaki çıktıyı alırız

map[fatma:31]

Map’de olmayan bir elemanın değerini istediğimizde hatayla karşılaşmadığımız gibi, olmayan bir elemanı silmeye çalışırsak da kodumuz sessiz sedasız çalışmaya devam edecektir.

Özetle; Map’de olmayan bir elemana değer atamaya çalışırsanız hata alırsınız. Olmayan bir elemanın değerini sorgular veya silmeye çalışırsanız hata almazsınız.

Go Map’lerini döngü içerisinde kullanmak

Son olarak bir Go Map dizisini döngü içerisinde nasıl kullanacağımıza bakalım. Bunun için aşağıdaki örnekteki gibi range anahtar kelimesini kullanabiliriz.

package main

import (
    "fmt"
)

func main() {
    studentsAge := make(map[string]int)
    studentsAge["selim"] = 32
    studentsAge["fatma"] = 31
    for name, age := range studentsAge {
        fmt.Printf("%s\t%d\n", name, age)
    }
}

Bu kodu çalıştırdığımızda şu çıktıyı alırız;

selim    32
fatma    31

Range ile iki değer döner, bunlardan ilki map elemanın anahtarı (key), ikincisi ise o anahtara ait değer (value) olacaktır.

Eğer anahtar ile ilgilenmiyorsanız döngüyü for _, age := range studentsAge şeklinde çağırarak ilk dönen değeri görmezden gelebilirsiniz. Eğer sadece anahtar ile ilgileniyorsanız, değeri görmezden gelmek için _ kullanmanıza gerek yok. for name := range studentsAge şeklinde tek bir değere atama yaparsanız sadece anahtar döndürülecek, değer görmezden gelinecektir.

Bir sonraki yazıda OOP temelli bir programlama dilinden Go’ya geçenleri ‘kısmen’ rahatlatacak ve bize büyük esneklik sağlayacak “Struct” tipinden bahsedeceğiz.

Bu Yazıda Yapılan Değişiklikler
  • 11.05.2022: Yazı özeti düzenlendi.