Tek örnekte slice
Slice, bir diziye açılan büyüyebilen bir görünümdür. Bir literal ya da make ile oluşturur, append ile büyütür ve boyutunu len ile okursunuz.
[]string'in köşeli parantezlerinde uzunluk yoktur. Boyutu sabit olan [4]string gibi bir dizi tipinden farkı budur.
Slice nedir
Bir slice değeri üç alanlı küçük bir başlıktır:
- arka dizideki bir elemana işaret eden bir pointer
- bir uzunluk: slice'ın kaç elemanı görebildiği
- bir kapasite: o pointer'dan arka dizinin sonuna kadar kaç eleman olduğu
Bir slice'ı kopyalamak (atamak, bir fonksiyona aktarmak) yalnızca bu başlığı kopyalar. İki kopya da aynı elemanlara işaret eder. Neredeyse her slice sürprizi bunu unutmaktan gelir.
make: uzunluk ve kapasite
make([]T, len, cap) bir arka dizi ayırır ve onun üzerinde bir slice döndürür. Kapasite isteğe bağlıdır ve varsayılan olarak uzunluğa eşittir.
Yaygın bir karışıklık: make([]int, 5) ardından beş append çağrısı size ilk beşi sıfır olan on eleman verir. Eleman eklemeyi planlıyorsanız make([]int, 0, 5), indeksle atamayı planlıyorsanız make([]int, 5) kullanın.
Son boyutu bildiğinizde kapasiteyi önceden ayırmak tekrarlanan büyümeyi önler. Bu bir optimizasyondur, zorunluluk değil.
append ve büyüme
append, elemanları mevcut uzunluğun ardına ekler. Kapasite yeterliyse yerinde yazar. Değilse daha büyük bir dizi ayırır, eski elemanları oraya kopyalar ve yeni diziye işaret eden bir slice döndürür.
Kapasite, slice küçükken kabaca iki katına çıkar, 256 elemanı geçtikten sonra ise daha küçük bir çarpanla (1.25x'e doğru) büyür. Kesin sayılar Go sürümleri arasında değişmiş bir çalışma zamanı ayrıntısıdır, bu yüzden asla onlara bağımlı kod yazmayın.
append farklı bir dizi döndürebileceği için sonucunu kullanmak zorundasınız:
append(s, 4) // compile error: append(s, 4) (value of type []int) is not used
s = append(s, 4) // correct
Bütün bir slice'ı eklemek açma söz dizimini kullanır: s = append(s, other...).
Dilimleme arka diziyi paylaşır
s[low:high], aynı dizi üzerinde low indeksinden başlayıp high indeksinden hemen önce biten yeni bir slice başlığı oluşturur. Sınırlardan herhangi biri yazılmayabilir. Hiçbir eleman kopyalanmaz.
Çıktı:
[10 99 30 40 50]
3 4
Dilimlemeyi ucuz yapan bu paylaşımdır. En ünlü slice hatasının kaynağı da odur.
append paylaşım (aliasing) hatası
Bir alt slice'ın boş kapasitesi varsa ona eleman eklemek, üst slice'ın dizisine, üst slice'ın hâlâ kullandığı elemanların üzerine yazar.
Çıktı:
[1 2 100 4 5]
[1 2 100]
[cmd test] [cmd test]
a ve b, "cmd"'den sonraki dizi yuvasını paylaşır, bu yüzden ikinci append ilkinin üzerine yazdı. Kod düzgün görünür ve kapasite tesadüfen dolu olduğunda çalışır; bu hatanın yalnızca bazen ortaya çıkmasının nedeni budur.
İki çözüm:
- Tam slice ifadesi
s[low:high:max], kapasiteyimax-lowile sınırlar. Boş kapasite olmadığında bir sonrakiappendyeni bellek ayırmak zorundadır.first := base[:2:2]ilk örneği güvenli hale getirir. - Bir slice çağrıdan uzun yaşayacaksa ya da bağımsız olarak büyütülecekse açıkça kopyalayın:
slices.Clone(prefix)ya daappend([]string(nil), prefix...).
İlgili bir tuzak: devasa bir dizinin küçük bir slice'ı, dizinin tamamını çöp toplayıcı için canlı tutar. 100 MB'lık bir dosyayı okuyup data[:10]'u tutarsanız 100 MB bellekte kalır. Tuttuğunuz kısmı klonlayın.
copy
Yerleşik copy(dst, src), min(len(dst), len(src)) eleman kopyalar ve bu sayıyı döndürür. dst'yi asla büyütmez.
copy çakışan kaynak ve hedefi doğru ele alır, bu yüzden copy(s[1:], s) elemanları bozmadan sağa kaydırır.
slices paketi
Go 1.21'den beri standart slices paketi, eskiden elle yazdığınız işlemler için generic yardımcılar sunar.
| Fonksiyon | Ne yapar |
|---|---|
Contains, Index | bir değer bulur (ContainsFunc, IndexFunc bir koşul fonksiyonu alır) |
Sort, SortFunc, SortStableFunc | yerinde sıralar |
BinarySearch | sıralı bir slice'ta arar |
Insert, Delete, DeleteFunc | ekler ya da siler, yeni slice'ı döndürür |
Compact | ardışık tekrarları kaldırır |
Equal, Compare | eleman eleman karşılaştırır |
Clone, Reverse, Max, Min | adlarının söylediğini |
Collect, Sorted, Values, All | iterator'larla çalışır (Go 1.23) |
Delete ve Insert, append gibi atamanız gereken bir slice döndürür. Sıralamanın kendi sayfası var: sıralama.
İndeksle dönerken eleman silmek bazı elemanları atlar. Bunun yerine slices.DeleteFunc kullanın:
s = slices.DeleteFunc(s, func(n int) bool { return n%2 == 0 }) // drop evens
2 boyutlu slice'lar
Slice'lardan oluşan bir slice, satırlarının uzunluğu farklı olabilen bir ızgara verir. Her satır ayrı ayrı ayrılmalıdır.
İçteki make'i unutmak her satırı nil bırakır ve grid[1][2] = 7, index out of range [2] with length 0 ile panic olur.
nil slice'lar ve boş slice'lar
Nil bir slice len, cap, range ve append için boş bir slice gibi davranır, bu yüzden sıfır durum olarak var s []T'yi tercih edin. Yalnızca fark önemliyse []T{} ile başlatın; bu da çoğunlukla JSON çıktısıdır (null ile []). Boşluğu s == nil ile değil, len(s) == 0 ile kontrol edin.
Slice'lar ve fonksiyonlar
Bir slice alan fonksiyon elemanlarını değiştirebilir ve çağıran değişiklikleri görür. Çağıranın uzunluğunu değiştiremez, çünkü başlığın bir kopyasını almıştır. Eleman ekleyen bir fonksiyon yeni slice'ı döndürmelidir:
func addAll(s []int, vals ...int) []int {
return append(s, vals...)
}
append, slices.Delete ve slices.Insert'ün hepsinin bir slice döndürmesinin nedeni budur.
Sık yapılan hatalar
append'in sonucunu yok saymak. Tamamen atıldığında derlenmez, amaappend(s, x)'i kullanmaya devam ettiğinizden farklı bir değişkene atamak bir mantık hatasıdır.- Tek bir tabandan iki append. Depolamayı paylaşabilirler. Tabanı klonlayın ya da tam slice ifadesi kullanın.
make([]T, n)ardındanappend. Başta n sıfır bırakır.- Index out of range.
s[len(s)]panic olur. Son elemans[len(s)-1]'dir. - Slice'ları
==ile karşılaştırmak. Yalnızcas == nilderlenir.slices.Equalkullanın.
Sıkça Sorulan Sorular
Go'da bir slice'ın uzunluğu ile kapasitesi arasındaki fark nedir?
len(s), slice'ın şu anda tuttuğu eleman sayısıdır. cap(s), slice'ın ilk elemanından başlayarak arka diziye sığan eleman sayısıdır. append, boş kapasite varsa oraya yazar, yoksa yeni ve daha büyük bir dizi ayırır.
Go'da bir slice'a nasıl eleman eklenir?
Yerleşik append'i çağırın ve sonucu geri atayın: s = append(s, x). Birkaç değer (append(s, 1, 2, 3)) ya da başka bir slice (append(s, other...)) ekleyebilir. Dönüş değerini her zaman kullanın, çünkü append yeni bir diziye işaret eden bir slice döndürebilir.
Go'da bir slice'tan eleman nasıl silinir?
Sonraki elemanları aşağı kaydırıp kısalmış slice'ı döndüren slices.Delete(s, i, i+1) (Go 1.21) kullanın. Go 1.22'den beri boşalan kuyruk yuvalarını da sıfırlar. Sıra önemli değilse son elemanla yer değiştirip kırpın: s[i] = s[len(s)-1]; s = s[:len(s)-1].
Go'da bir slice'ın bir değeri içerip içermediği nasıl kontrol edilir?
Standart slices paketinden (Go 1.21) slices.Contains(s, v) kullanın. slices.Index(s, v) konumu ya da -1 döndürür, slices.ContainsFunc ise bir koşul fonksiyonu alır. Büyük veride tekrarlanan aramalar için bunun yerine bir map oluşturun.
Go'da nil slice ile boş slice arasındaki fark nedir?
var s []int nil'dir; s := []int{} boştur ama nil değildir. İkisinin de uzunluğu 0'dır, ikisi de len, range ve append ile çalışır. Fark s == nil kontrollerinde ve nil bir slice'ı null, boş bir slice'ı [] olarak kodlayan encoding/json'da ortaya çıkar.