Kapak fotoğraf Lisa tarafından Pexels ‘de paylaşılmıştır.
Kod örnekleri Microsoft Learn – Explore Slices
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 inceleyeceğiz.
Bu yazının konusu da Go’da en çok kullanacağımız veri tiplerinden biri olan Slice. Slice’larda Array’ler gibi aynı tipteki bir dizi elemanı saklamaya yarayan veri tipleri. Fakat Array’lerin boyutu sabitken, Slice’ların boyutu çalışma esnasında değiştirilebiliyor.
Slice’lar Array ya da bir başka Slice’ı esas alan veri tipleri. Temel olarak bir Array ya da bir başka Slice’ın bir bölümüne atıfta bulunan, onun bir dilimini alan yapılar. 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:
- Atıfta bulunulan dizideki erişilmek istenen ilk elemanın işaretçisi (pointer). Bu illa ki atıfta bulunulan dizinin ilk elemanı olmak zorunda değildir. ( Ama olabilir de )
- Slice ‘ın boyutu
- Slice ‘ın kapasitesi
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]
Botu : 12
Kapasite: 12
Burada aklınıza şu gelmiş olabilir: “Slice boyutu ve kapasitesinin farkı nedir?” Bunu bir örnekle anlatmak daha kolay oalcaktı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 olacak;
[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ı. Slice’ı tanımlarken başlangıç pozisyonunu ve uzunluğunu belirtiyoruz. Slice boyutu belirlediğimiz boyutken, kapasitesi açık uçlu diyebiliriz ve slice boyutumuzun nereye kadar uzayabileceğini gösteriyor. Kapasite değeri Slice’ı nereye kadar uzatabileceğimizi gösteriyor. Ö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 çıkartabiliyoruz..
[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 önemli özellikleri de var. En önemlisi Slice’ları boyutu Array’ler gibi sabit değil, aksine dinamik. Bir Slice oluşturduktan sonra bu Slice’a yeni elemanlar ekleyebilir ve oluşturduktan sonra slice boyutunu değiştirebilirsiniz.
Go’da slice’a bir eleman eklemek için append(slice, eleman)
komutunu kullanıyoruz. Append foksiyonuna değiştirilecek slice’ı ve eklenecek elemanı gönderdiğimizde bize yeni eleman eklenmiş slice’ı geri döndürecektir.
append fonksiyonunun nasıl çalıştığını bir kodla inceleyelim;
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 üçüncü adımdan sonra slice kapasitesindeki değişimdir. Farkındaysanız 2 olan slice boyutu yetersiz geldiği için otomatik olarak arttırılıyor, önce 4’e, sonra 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 çıkartıyor.
Slice’dan eleman çıkartmak
Go’da slice’lardan eleman çıkartmak için hazır bir fonksiyon yok. Bunun yerine işlemi çıkartacağımız elemanın olmadığı yeni bir slice oluşturarak yapıyoruz.
Örnek kod ve çıktısı:
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)
}
}
Ö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. Örneğin;
slice2 := make([]string, 3)
copy(slice2, letters[1:4])
Neden Slice’ın kopyasını oluşturmalıyız? Bir slice’da değişiklik yaptığınızda aslında slice’ı değil, slice’ın baz aldığı array’i değiştirirsiniz. Eğer o array’i esas alan başka slice’larınız varsa onlar da değişmiş olur. Bunu istemediğimiz durumlarda Slice’ın kopyasını oluşturup onun üzerinde çalışabilirsiniz.
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.