Skip to content

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

go
// Contoh benar
var a [5]int

// Contoh salah
l := 1
var b [l]int

Mari inisialisasi array integer dengan panjang 5

go
var nums [5]int

Juga dapat menggunakan elemen untuk inisialisasi

go
nums := [5]int{1, 2, 3}

Dapat membiarkan kompiler secara otomatis menyimpulkan panjang

go
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 array

Juga dapat melalui fungsi new mendapatkan pointer

go
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

go
length := 5 // Ini adalah variabel
var nums [length]int

length adalah variabel oleh karena itu tidak dapat digunakan untuk menginisialisasi panjang array berikut adalah contoh benar

go
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 digunakan

Penggunaan

Selama ada nama array dan indeks dapat mengakses elemen yang sesuai dalam array.

go
fmt.Println(nums[0])

Sama juga dapat memodifikasi elemen array

go
nums[0] = 1

Juga dapat melalui fungsi built-in len untuk mengakses jumlah elemen array

go
len(nums)

Fungsi built-in cap untuk mengakses kapasitas array kapasitas array sama dengan panjang array kapasitas memiliki arti untuk slice.

go
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

go
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]
go
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
[]int

Jika 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

go
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

go
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

go
var nums []int // nilai
nums := []int{1, 2, 3} // nilai
nums := make([]int, 0, 0) // nilai
nums := new([]int) // pointer

Dapat 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.

go
func append(slice []Type, elems ...Type) []Type

Pertama membuat slice kosong dengan panjang 0 kapasitas 0 lalu menyisipkan beberapa elemen di ekor terakhir mengeluarkan panjang dan kapasitas.

go
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

go
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

Menyisipkan elemen dari kepala

go
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

go
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

go
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

go
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

Menghapus n elemen dari kepala

go
nums = nums[n:]
fmt.Println(nums) //n=3 [4 5 6 7 8 9 10]

Menghapus n elemen dari ekor

go
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

go
nums = append(nums[:i], nums[i+n:]...)
fmt.Println(nums)// i=2, n=3, [1 2 6 7 8 9 10]

Menghapus semua elemen

go
nums = nums[:0]
fmt.Println(nums) // []

Copy

Slice saat copy perlu memastikan slice target memiliki panjang yang cukup misalnya

go
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

go
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

go
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

go
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即可.

go
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

go
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

go
s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} // cap = 9
s2 := s1[3:4] // cap = 9 - 3 = 6

Maka 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

go
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

go
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

go
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

go
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.

Golang by www.golangdev.cn edit