Teknik Detaylar

Go Veri Tipleri: Slice

← Teknik Detaylar
2021-10-10 ~ 2026-06-21 · 5 dk okuma
Go Veri Tipleri: Slice
Bu yazıyı yapay zekâ ile tartış
Sayfayı kopyala

💡 Özet (TL;DR):

  • Nedir: Go dilinde dizilerin (array) aksine dinamik olarak büyüyüp küçülebilen, arka planda bir diziyi (underlying array) referans alan esnek bir veri yapısıdır.
  • 3 Temel Bileşeni: İşaretçi (pointer - verinin başladığı adres), Boyut (length - slice'taki eleman sayısı) ve Kapasite (capacity - slice'ın genişleyebileceği maksimum eleman sayısı).
  • Kritik Davranış: Slice kapasitesi dolduğunda, Go arka planda yeni ve iki katı büyüklükte bir dizi tahsis eder (reallocation) ve eski verileri oraya kopyalar.

Bir önceki yazıda Go'da string, integer, float, complex, boolean ve array veri tiplerini inceledik. Bundan sonraki veri tiplerimiz (slice, map, struct) biraz daha geniş içerikli olacağı için ayrı yazılarda ele alacağız.

Bu yazının konusu da Go'da en çok kullanacağımız veri tiplerinden biri olan Slice. Slice'lar da Array'ler gibi aynı tipteki bir dizi elemanı saklamaya yarayan veri tipleridir. Fakat Array'lerin boyutu sabitken, Slice'ların boyutu çalışma esnasında değiştirilebilir.

Slice'lar, bir Array ya da bir başka Slice'ı temel alan veri tipleridir. Temel olarak bir Array ya da bir başka Slice'ın bir bölümüne atıfta bulunan, onun bir dilimini alan yapılardır. Kolaylık olması açısından buna "atıfta bulunulan dizi" diyelim. Bir slice, atıfta bulunduğu dizinin sadece bir bölümünü ya da tamamını temsil ediyor olabilir.

Slice'lar 3 bileşenden oluşurlar:

  • Pointer (İşaretçi): Atıfta bulunulan dizideki erişilmek istenen ilk elemanın işaretçisidir. Bu, atıfta bulunulan dizinin ilk elemanı olmak zorunda değildir.
  • Length (Boyut): Slice'ın içerdiği eleman sayısıdır.
  • Capacity (Kapasite): Slice'ın temel aldığı dizinin başlangıç elemanından itibaren alabileceği maksimum eleman sayısıdır.

Go'da Array ve Slice Karşılaştırması

ÖzellikArray (Dizi)Slice (Dilim)
Boyut TanımıSabittir, tanımlandıktan sonra değiştirilemez (Örn: [5]int).Dinamiktir, çalışma zamanında büyüyebilir (Örn: []int).
Hafıza YönetimiDeğer tipidir (value type). Fonksiyona geçildiğinde kopyalanır.Referans tipidir (reference type). Arka plandaki diziye işaret eder.
Eleman EklemeYapılamaz, boyutu sabittir.append() fonksiyonu ile dinamik olarak eklenir.
Kapasite KontrolüGerek yoktur, boyutu neyse kapasitesi odur.cap() fonksiyonu ile öğrenilir, dolduğunda Go otomatik artırır.

Slice Nasıl Tanımlanır?

En basit haliyle, büyüklüğünü belirtmediğiniz bir array tanımladığınızda bir slice tanımlamış olursunuz.

Örnek bir slice tanımı yapalım:

package main

import "fmt"

func main() {
    months := []string{"Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"}
    fmt.Println("Aylar   :", months)
    fmt.Println("Boyut   :", len(months))
    fmt.Println("Kapasite:", cap(months))
}

Kodu çalıştırdığımızda şu sonucu alacağız:

Aylar   : [Ocak Şubat Mart Nisan Mayıs Haziran Temmuz Ağustos Eylül Ekim Kasım Aralık]
Boyut   : 12
Kapasite: 12

Burada aklınıza şu gelmiş olabilir: "Slice boyutu ve kapasitesinin farkı nedir?" Bunu bir örnekle anlatmak daha kolay olacaktır:

package main

import "fmt"

func main() {
    months := []string{"Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"}
    quarter1 := months[0:3]
    quarter2 := months[3:6]
    quarter3 := months[6:9]
    quarter4 := months[9:12]
    fmt.Println(quarter1, len(quarter1), cap(quarter1))
    fmt.Println(quarter2, len(quarter2), cap(quarter2))
    fmt.Println(quarter3, len(quarter3), cap(quarter3))
    fmt.Println(quarter4, len(quarter4), cap(quarter4))
}

Kodun çıktısı şöyle olacaktır:

[Ocak Şubat Mart] 3 12
[Nisan Mayıs Haziran] 3 9
[Temmuz Ağustos Eylül] 3 6
[Ekim Kasım Aralık] 3 3

Dikkat ederseniz her birinin boyutu 3 iken, kapasiteleri farklıdır. Slice'ı tanımlarken başlangıç pozisyonunu ve uzunluğunu belirtiyoruz. Slice boyutu belirlediğimiz boyutken, kapasitesi açık uçludur ve slice boyutumuzun nereye kadar uzayabileceğini gösterir. Örneğin:

package main

import "fmt"

func main() {
    months := []string{"Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"}
    quarter2 := months[3:6]
    quarter2Extended := quarter2[:4]
    fmt.Println(quarter2, len(quarter2), cap(quarter2))
    fmt.Println(quarter2Extended, len(quarter2Extended), cap(quarter2Extended))
}

Bu kodla quarter2'yi ilk elemanından başlatarak uzunluğunu 3'ten 4'e çıkarabiliyoruz:

[Nisan Mayıs Haziran] 3 9
[Nisan Mayıs Haziran Temmuz] 4 9

Slice'a Eleman Eklemek

Şu ana kadar Slice'ın Array'lere benzer olan kısımlarını ve bir Array'i temel alarak nasıl slice oluşturabileceğimizi öğrendik. Fakat Slice'ın Array'e göre en önemli farkı dinamik olmasıdır. Bir Slice oluşturduktan sonra bu Slice'a yeni elemanlar ekleyebilir ve boyutunu değiştirebilirsiniz.

Go'da slice'a bir eleman eklemek için append(slice, eleman) fonksiyonunu kullanıyoruz. Append fonksiyonuna değiştirilecek slice'ı ve eklenecek elemanı gönderdiğimizde, bize yeni eleman eklenmiş slice'ı geri döndürecektir.

package main

import "fmt"

func main() {
    var numbers []int
    for i := 0; i < 10; i++ {
        numbers = append(numbers, i)
        fmt.Printf("%d\tcap=%d\t%v\n", i, cap(numbers), numbers)
    }
}

Bu kodun çıktısı da şöyle olacaktır:

0       cap=1   [0]
1       cap=2   [0 1]
2       cap=4   [0 1 2]
3       cap=4   [0 1 2 3]
4       cap=8   [0 1 2 3 4]
5       cap=8   [0 1 2 3 4 5]
6       cap=8   [0 1 2 3 4 5 6]
7       cap=8   [0 1 2 3 4 5 6 7]
8       cap=16  [0 1 2 3 4 5 6 7 8]
9       cap=16  [0 1 2 3 4 5 6 7 8 9]

Bu çıktıda dikkat çekici olan, slice kapasitesindeki değişimdir. Farkındaysanız slice boyutu yetersiz geldiğinde kapasite otomatik olarak artırılıyor; önce 1'den 2'ye, sonra 4'e, 8'e ve en son olarak da 16'ya çıkıyor. Görüldüğü üzere slice kapasitesi yetersiz olduğunda Go otomatik olarak kapasiteyi iki katına çıkarır.


Slice'tan Eleman Çıkarmak

Go'da slice'lardan eleman çıkarmak için hazır bir fonksiyon yoktur. Bunun yerine işlemi, çıkaracağımız elemanın olmadığı yeni bir slice oluşturarak yaparız:

package main

import "fmt"

func main() {
    letters := []string{"A", "B", "C", "D", "E"}
    remove := 2

    if remove < len(letters) {
        fmt.Println("Önce:", letters)
        letters = append(letters[:remove], letters[remove+1:]...)
        fmt.Println("Sonra:", letters)
    }
}

Çıktı:

Önce: [A B C D E]
Sonra: [A B D E]

Slice'ların Kopyalarını Oluşturmak

Go'da slice'ların kopyalarını oluşturmak için hazır copy(dst, src []Type) fonksiyonunu kullanabilirsiniz. Copy fonksiyonuna hedef slice'ı ve kaynak slice'ı vererek kopyalayabilirsiniz:

slice2 := make([]string, 3)
copy(slice2, letters[1:4])

Neden Slice'ın kopyasını oluşturmalıyız? Bir slice'ta değişiklik yaptığınızda aslında slice'ın kendisini değil, arka planda işaret ettiği temel diziyi (array) değiştirirsiniz. Eğer o diziyi esas alan başka slice'larınız varsa onlar da değişmiş olur. Bunu istemediğimiz durumlarda Slice'ın bağımsız bir kopyasını oluşturup onun üzerinde çalışmalıyız.

Bir sonraki yazıda Go'da Map veri tipini inceleyeceğiz.


Bu Yazıda Yapılan Değişiklikler
  • 11.05.2022: Yazı özeti düzenlendi.
  • 21.06.2026: Yazım imla hataları (Boyut typo'su, bağlaç yazımları vb.) düzeltildi, kod renklendirmeleri eklendi, Go Array ve Slice kıyaslama tablosu ile TL;DR özet paneli eklendi.