Slice
Di Go array dan slice keduanya terlihat hampir sama persis tetapi fungsi memiliki perbedaan yang cukup besar array adalah struktur data dengan panjang tetap panjang setelah ditentukan tidak dapat diubah sedangkan slice adalah dengan panjang tidak tetap slice akan melakukan ekspansi sendiri saat kapasitas tidak cukup.
Array
Jika sebelumnya sudah mengetahui panjang data yang akan disimpan dan selanjutnya tidak ada kebutuhan ekspansi dapat mempertimbangkan menggunakan array array di Go adalah tipe nilai bukan referensi bukan pointer yang menunjuk ke elemen kepala.
TIP
Array sebagai tipe nilai saat array diberikan sebagai parameter ke fungsi karena Go fungsi adalah passing nilai jadi akan menyalin seluruh array.
Inisialisasi
Array saat dideklarasikan panjang hanya dapat berupa konstanta tidak dapat berupa variabel Anda tidak dapat mendeklarasikan variabel lalu menggunakan variabel sebagai nilai panjang array
// Contoh benar
var a [5]int
// Contoh salah
l := 1
var b [l]intMari inisialisasi array integer dengan panjang 5
var nums [5]intJuga dapat menggunakan elemen untuk inisialisasi
nums := [5]int{1, 2, 3}Dapat membiarkan kompiler secara otomatis menyimpulkan panjang
nums := [...]int{1, 2, 3, 4, 5} // setara dengan nums := [5]int{1, 2, 3, 4, 5} elipsis harus ada jika tidak yang dihasilkan adalah slice bukan arrayJuga dapat melalui fungsi new mendapatkan pointer
nums := new([5]int)Beberapa cara di atas akan mengalokasikan memori dengan ukuran tetap untuk nums perbedaannya hanya cara terakhir yang得到的 nilai adalah pointer.
Saat inisialisasi array perlu diperhatikan panjang harus berupa ekspresi konstanta jika tidak tidak akan bisa dikompilasi ekspresi konstanta yaitu hasil akhir ekspresi adalah konstanta contoh error adalah sebagai berikut
length := 5 // Ini adalah variabel
var nums [length]intlength adalah variabel oleh karena itu tidak dapat digunakan untuk menginisialisasi panjang array berikut adalah contoh benar
const length = 5
var nums [length]int // konstanta
var nums2 [length + 1]int // ekspresi konstanta
var nums3 [(1 + 2 + 3) * 5]int // ekspresi konstanta
var nums4 [5]int // yang paling umum digunakanPenggunaan
Selama ada nama array dan indeks dapat mengakses elemen yang sesuai dalam array.
fmt.Println(nums[0])Sama juga dapat memodifikasi elemen array
nums[0] = 1Juga dapat melalui fungsi built-in len untuk mengakses jumlah elemen array
len(nums)Fungsi built-in cap untuk mengakses kapasitas array kapasitas array sama dengan panjang array kapasitas memiliki arti untuk slice.
cap(nums)Pemotongan
Format pemotongan array adalah arr[startIndex:endIndex] interval pemotongan adalah kiri tertutup kanan terbuka. Dan array setelah dipotong akan menjadi tipe slice. Contohnya sebagai berikut
nums := [5]int{1, 2, 3, 4, 5}
nums[:] // rentang sub slice [0,5) -> [1 2 3 4 5]
nums[1:] // rentang sub slice [1,5) -> [2 3 4 5]
nums[:5] // rentang sub slice [0,5) -> [1 2 3 4 5]
nums[2:3] // rentang sub slice [2,3) -> [3]
nums[1:3] // rentang sub slice [1,3) -> [2 3]func main() {
arr := [5]int{1, 2, 3, 4, 5}
fmt.Printf("%T\n", arr)
fmt.Printf("%T\n", arr[1:2])
}Output
[5]int
[]intJika ingin mengonversi array ke tipe slice tidak perlu parameter untuk slicing slice yang dikonversi dengan array asli menunjuk ke memori yang sama memodifikasi slice akan menyebabkan perubahan konten array asli
func main() {
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[:]
slice[0] = 0
fmt.Printf("array: %v\n", arr)
fmt.Printf("slice: %v\n", slice)
}Output
array: [0 2 3 4 5]
slice: [0 2 3 4 5]Jika ingin memodifikasi slice yang telah dikonversi disarankan menggunakan cara di bawah ini untuk konversi
func main() {
arr := [5]int{1, 2, 3, 4, 5}
slice := slices.Clone(arr[:])
slice[0] = 0
fmt.Printf("array: %v\n", arr)
fmt.Printf("slice: %v\n", slice)
}Output
array: [1 2 3 4 5]
slice: [0 2 3 4 5]Slice
Aplikasi slice di Go jauh lebih luas daripada array digunakan untuk menyimpan data dengan panjang tidak diketahui dan selanjutnya mungkin sering menyisipkan dan menghapus elemen.
Inisialisasi
Cara inisialisasi slice adalah sebagai berikut
var nums []int // nilai
nums := []int{1, 2, 3} // nilai
nums := make([]int, 0, 0) // nilai
nums := new([]int) // pointerDapat dilihat perbedaan slice dan array dalam penampilan hanya kurang satu panjang inisialisasi. Biasanya direkomendasikan menggunakan make untuk membuat slice kosong hanya saja untuk slice fungsi make menerima tiga parameter tipe panjang kapasitas. Berikan contoh untuk menjelaskan perbedaan panjang dan kapasitas misalkan ada seember air air tidak penuh tinggi ember adalah kapasitas ember mewakili total dapat menampung berapa tinggi air sedangkan tinggi air dalam ember mewakili panjang tinggi air pasti kurang dari atau sama dengan tinggi ember jika tidak air akan tumpah. Jadi panjang slice mewakili jumlah elemen dalam slice kapasitas slice mewakili slice total dapat menampung berapa banyak elemen perbedaan terbesar slice dan array adalah kapasitas slice akan secara otomatis berkembang sedangkan array tidak lebih detail pergi ke Referensi Manual - Panjang dan Kapasitas.
TIP
Implementasi底层 slice tetap adalah array adalah tipe referensi dapat sederhana dipahami adalah pointer yang menunjuk ke array底层 (pada dasarnya slice di Go adalah struct berisi pointer yang menunjuk ke array底层 nilai panjang nilai kapasitas). Oleh karena itu slice saat diberikan sebagai parameter fungsi tidak menyalin array底层 modifikasi pada slice yang diteruskan dalam fungsi akan tercermin di slice asli.
Melalui cara var nums []int untuk mendeklarasikan slice nilai default adalah nil jadi tidak akan mengalokasikan memori untuknya dan saat menggunakan make untuk inisialisasi disarankan mengalokasikan kapasitas yang cukup dapat secara efektif mengurangi konsumsi memori ekspansi selanjutnya.
Penggunaan
Penggunaan dasar slice sama persis dengan array perbedaannya hanya slice dapat secara dinamis mengubah panjang lihat beberapa contoh di bawah ini.
Slice dapat melalui fungsi append mengimplementasikan banyak operasi tanda tangan fungsi adalah sebagai berikut slice adalah slice target yang akan ditambahkan elemen elems adalah elemen yang akan ditambahkan nilai pengembalian adalah slice setelah ditambahkan.
func append(slice []Type, elems ...Type) []TypePertama membuat slice kosong dengan panjang 0 kapasitas 0 lalu menyisipkan beberapa elemen di ekor terakhir mengeluarkan panjang dan kapasitas.
nums := make([]int, 0, 0)
nums = append(nums, 1, 2, 3, 4, 5, 6, 7)
fmt.Println(len(nums), cap(nums)) // 7 8 dapat dilihat panjang dan kapasitas tidak konsisten.Ukuran kapasitas buffer yang disisakan slice baru ada aturan tertentu. Sebelum pembaruan versi golang1.18 sebagian besar artikel di internet mendeskripsikan strategi ekspansi slice seperti ini Saat kapasitas slice asli kurang dari 1024 kapasitas slice baru menjadi 2 kali lipat dari aslinya saat kapasitas slice asli melebihi 1024 kapasitas slice baru menjadi 1.25 kali lipat dari aslinya. Setelah pembaruan versi 1.18 strategi ekspansi slice berubah menjadi Saat kapasitas slice asli (oldcap) kurang dari 256 kapasitas slice baru (newcap) adalah 2 kali lipat dari aslinya saat kapasitas slice asli melebihi 256 kapasitas slice baru newcap = oldcap+(oldcap+3*256)/4
Menyisipkan Elemen
Penyisipan elemen slice juga perlu dikombinasikan dengan fungsi append untuk digunakan ada slice sebagai berikut
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}Menyisipkan elemen dari kepala
nums = append([]int{-1, 0}, nums...)
fmt.Println(nums) // [-1 0 1 2 3 4 5 6 7 8 9 10]Menyisipkan elemen dari indeks tengah i
nums = append(nums[:i+1], append([]int{999, 999}, nums[i+1:]...)...)
fmt.Println(nums) // i=3 [1 2 3 4 999 999 5 6 7 8 9 10]Menyisipkan elemen dari ekor adalah penggunaan paling asli dari append
nums = append(nums, 99, 100)
fmt.Println(nums) // [1 2 3 4 5 6 7 8 9 10 99 100]Menghapus Elemen
Penghapusan elemen slice perlu dikombinasikan dengan fungsi append untuk digunakan ada slice sebagai berikut
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}Menghapus n elemen dari kepala
nums = nums[n:]
fmt.Println(nums) //n=3 [4 5 6 7 8 9 10]Menghapus n elemen dari ekor
nums = nums[:len(nums)-n]
fmt.Println(nums) //n=3 [1 2 3 4 5 6 7]Menghapus n elemen mulai dari posisi indeks tengah i
nums = append(nums[:i], nums[i+n:]...)
fmt.Println(nums)// i=2, n=3, [1 2 6 7 8 9 10]Menghapus semua elemen
nums = nums[:0]
fmt.Println(nums) // []Copy
Slice saat copy perlu memastikan slice target memiliki panjang yang cukup misalnya
func main() {
dest := make([]int, 0)
src := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(src, dest)
fmt.Println(copy(dest, src))
fmt.Println(src, dest)
}[1 2 3 4 5 6 7 8 9] []
0
[1 2 3 4 5 6 7 8 9] []Mengubah panjang menjadi 10 output adalah sebagai berikut
[1 2 3 4 5 6 7 8 9] [0 0 0 0 0 0 0 0 0 0]
9
[1 2 3 4 5 6 7 8 9] [1 2 3 4 5 6 7 8 9 0]Iterasi
Iterasi slice sama persis dengan array loop for
func main() {
slice := []int{1, 2, 3, 4, 5, 7, 8, 9}
for i := 0; i < len(slice); i++ {
fmt.Println(slice[i])
}
}Loop for range
func main() {
slice := []int{1, 2, 3, 4, 5, 7, 8, 9}
for index, val := range slice {
fmt.Println(index, val)
}
}Slice Multi Dimensi
Mari lihat contoh di bawah ini dokumentasi resmi juga ada penjelasan Effective Go - Slice Dua Dimensi
var nums [5][5]int
for _, num := range nums {
fmt.Println(num)
}
fmt.Println()
slices := make([][]int, 5)
for _, slice := range slices {
fmt.Println(slice)
}Hasil output adalah
[0 0 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]
[]
[]
[]
[]
[]Dapat dilihat sama-sama array dan slice dua dimensi struktur internalnya berbeda. Array saat inisialisasi panjang satu dan dua dimensi sudah tetap sedangkan panjang slice tidak tetap setiap slice dalam slice mungkin panjangnya berbeda jadi harus diinisialisasi secara terpisah bagian inisialisasi slice diubah menjadi kode di bawah ini即可.
slices := make([][]int, 5)
for i := 0; i < len(slices); i++ {
slices[i] = make([]int, 5)
}Hasil output akhir adalah
[0 0 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]Ekspresi Diperluas
TIP
Hanya slice yang dapat menggunakan ekspresi diperluas
Slice dan array keduanya dapat menggunakan ekspresi sederhana untuk melakukan pemotongan tetapi ekspresi diperluas hanya slice yang dapat digunakan fitur ini ditambahkan pada versi Go1.2 terutama untuk menyelesaikan masalah baca-tulis slice berbagi array底层 format utamanya adalah sebagai berikut perlu memenuhi hubungan low<= high <= max <= cap kapasitas slice yang dipotong menggunakan ekspresi diperluas adalah max-low
slice[low:high:max]low dan high tetap arti aslinya tidak berubah sedangkan max yang ditambahkan mengacu pada kapasitas maksimum misalnya contoh di bawah ini menghilangkan max maka kapasitas s2 adalah cap(s1)-low
s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} // cap = 9
s2 := s1[3:4] // cap = 9 - 3 = 6Maka ini akan ada masalah yang jelas s1 dan s2 berbagi array底层 yang sama saat melakukan baca-tulis pada s2 mungkin mempengaruhi data s1 kode berikut termasuk dalam situasi ini
s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} // cap = 9
s2 := s1[3:4] // cap = 9 - 3 = 6
s2 = append(s2, 1) // Menambahkan elemen baru karena kapasitas adalah 6 jadi tidak ada ekspansi langsung memodifikasi array底层
fmt.Println(s2)
fmt.Println(s1)Output akhir adalah
[4 1]
[1 2 3 4 1 6 7 8 9]Dapat dilihat jelas menambahkan elemen ke s2 tetapi juga memodifikasi s1 ekspresi diperluas adalah untuk menyelesaikan masalah seperti ini hanya perlu sedikit modifikasi即可 menyelesaikan masalah ini
func main() {
s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} // cap = 9
s2 := s1[3:4:4] // cap = 4 - 3 = 1
s2 = append(s2, 1) // Kapasitas tidak cukup mengalokasikan array底层 baru
fmt.Println(s2)
fmt.Println(s1)
}Sekarang hasil yang得到了 adalah normal
[4 1]
[1 2 3 4 5 6 7 8 9]clear
Di go1.21 menambahkan fungsi built-in clear clear akan mengubah semua nilai dalam slice menjadi nilai nol
package main
import (
"fmt"
)
func main() {
s := []int{1, 2, 3, 4}
clear(s)
fmt.Println(s)
}Output
[0 0 0 0]Jika ingin mengosongkan slice dapat
func main() {
s := []int{1, 2, 3, 4}
s = s[:0:0]
fmt.Println(s)
}Membatasi kapasitas setelah pemotongan ini dapat menghindari menutupi elemen berikutnya dari slice asli.
