Data di dalam program hampir tidak pernah berhenti di jumlah awalnya: daftar pesanan bertambah, hasil pembacaan bertubi-tubi, dan sisa ruang memori bisa jadi habis. Go menyediakan dua built-in untuk mengelola perubahan isi slice: append menambah elemen di akhir slice, dan copy memindahkan elemen antar-slice. Pada chapter ini kita membedah perilaku keduanya, termasuk kebiasaan wajib mereassign hasil append dan aturan jumlah elemen yang benar-benar tersalin oleh copy.
Menambah Elemen dengan append #
append(s, elemen) mengembalikan slice baru yang berisi semua elemen s diikuti elemen di akhir. Bila kapasitas s masih mencukupi, elemen baru menempati slot sisa; bila tidak, Go menyiapkan penyimpanan baru seperti yang kita amati pada chapter sebelumnya. Lebih dari satu elemen bisa ditambahkan sekaligus, dan slice lain bisa ditempel utuh dengan operator ...:
angka = append(angka, 4, 5) // dua elemen sekaligus
angka = append(angka, genap...) // tempel seluruh isi slice genapOperator ... di sini membuka (spread) slice genap menjadi daftar argumen sehingga setiap elemennya dikirim satu per satu ke append. Bentuk ini adalah cara standar menggabungkan dua slice dalam Go.
Selalu Reassign Hasil append #
append mengembalikan slice yang mungkin menunjuk ke penyimpanan berbeda dari s, sehingga idiom yang benar selalu menampung hasilnya kembali: s = append(s, elemen). Memanggil append(s, elemen) tanpa menampung hasilnya membuat elemen baru hilang dari pandangan, dan bila pemanggilan itu terjadi di luar kapasitas lama, s lama tidak berubah sama sekali. Idiom reassignment ini juga menjaga konsistensi saat penyimpanan baru dialokasikan dan elemen lama dipindahkan.
Menyalin Elemen dengan copy #
copy(dst, src) memindahkan elemen dari src ke dst dan mengembalikan n, jumlah elemen yang benar-benar tersalin. Jumlah tersebut adalah yang lebih kecil antara len(dst) dan len(src): tujuan yang lebih pendek hanya menampung sebagian awal sumber, sementara tujuan yang lebih panjang hanya terisi pada bagian awalnya. Kedua slice boleh beririsan pada penyimpanan yang sama — copy menangani penyalinan tumpang tindih secara benar, seperti memmove di bahasa C, sehingga pemakaian copy(geser[1:], geser[:4]) aman dipakai untuk menggeser elemen ke kanan.
Program Utuh #
Berkas append-copy-demo.go merangkai seluruh variasi di atas dalam satu program yang bisa dijalankan dengan go run append-copy-demo.go:
package main
import "fmt"
func main() {
// append satu elemen: hasil reassign kembali ke variabel.
angka := []int{1, 2}
angka = append(angka, 3)
fmt.Println("satu elemen :", angka)
// append beberapa elemen sekaligus.
angka = append(angka, 4, 5)
fmt.Println("dua elemen :", angka)
// Menempelkan slice lain dengan operator ...
genap := []int{6, 7}
angka = append(angka, genap...)
fmt.Println("ditempel :", angka)
fmt.Println("len :", len(angka))
// copy menyalin elemen dan mengembalikan jumlah elemen tersalin.
asal := []int{10, 20, 30, 40, 50}
tujuan := make([]int, 3)
n := copy(tujuan, asal)
fmt.Println("n tersalin :", n)
fmt.Println("tujuan :", tujuan)
// Tujuan lebih besar daripada sumber: hanya bagian awal yang terisi.
besar := make([]int, 5)
n2 := copy(besar, []int{90, 91})
fmt.Println("n2 tersalin :", n2)
fmt.Println("besar :", besar)
// copy pada area tujuan yang beririsan (overlap).
geser := []int{1, 2, 3, 4, 5}
copy(geser[1:], geser[:4])
fmt.Println("geser :", geser)
}Output yang Dihasilkan:
satu elemen : [1 2 3]
dua elemen : [1 2 3 4 5]
ditempel : [1 2 3 4 5 6 7]
len : 7
n tersalin : 3
tujuan : [10 20 30]
n2 tersalin : 2
besar : [90 91 0 0 0]
geser : [1 1 2 3 4]Mari kita telaah baris demi baris program di atas:
- Baris 7–8 (
angka = append(angka, 3)): satu elemen ditambahkan dan hasilnya direassign; setiap pemanggilanappenddi program ini memakai pola yang sama. - Baris 12 (
append(angka, 4, 5)): dua elemen masuk sekaligus dalam satu pemanggilan. - Baris 16 (
append(angka, genap...)): operator...membuka isigenapsehinggaangkabertambah dua elemen menjadi tujuh. - Baris 21–23 (
n := copy(tujuan, asal)): tujuan hanya berisi tiga slot, sehingganbernilai 3 dantujuanmenampung[10 20 30]— elemen 40 dan 50 sumber tidak ikut. - Baris 27–29 (
n2 := copy(besar, ...)): sumber hanya dua elemen, jadin2bernilai 2;besarterisi[90 91 0 0 0]dan tiga slot sisanya tetap zero value. - Baris 33 (
copy(geser[1:], geser[:4])): menyalin area yang beririsan — elemen[1 2 3 4]digeser satu posisi ke kanan menghasilkan[1 1 2 3 4], dancopymenyelesaikannya tanpa korupsi data meski sumber dan tujuan bertumpuk.
Perpaduan append untuk bertumbuh dan copy untuk memindahkan elemen menutup hampir seluruh kebutuhan manipulasi slice. Namun ada satu lapisan lagi yang belum kita bedah: ke mana persisnya elemen ditulis ketika append masih berada di dalam kapasitas, dan mengapa dua slice bisa saling memengaruhi. Chapter Backing Array
menjawab pertanyaan tersebut.
Referensi #
- Spesifikasi Go: Appending and copying slices
— definisi resmi
appenddancopy. - Go Blog: Arrays, slices (and strings): The mechanics of append
— pendalaman mekanisme
append. - Dokumentasi package slices
— utilitas standar pengganti pola manual (misalnya
slices.Concat).
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
append-copy-demo.go
Unduh berkaspackage main
import "fmt"
func main() {
// append satu elemen: hasil reassign kembali ke variabel.
angka := []int{1, 2}
angka = append(angka, 3)
fmt.Println("satu elemen :", angka)
// append beberapa elemen sekaligus.
angka = append(angka, 4, 5)
fmt.Println("dua elemen :", angka)
// Menempelkan slice lain dengan operator ...
genap := []int{6, 7}
angka = append(angka, genap...)
fmt.Println("ditempel :", angka)
fmt.Println("len :", len(angka))
// copy menyalin elemen dan mengembalikan jumlah elemen tersalin.
asal := []int{10, 20, 30, 40, 50}
tujuan := make([]int, 3)
n := copy(tujuan, asal)
fmt.Println("n tersalin :", n)
fmt.Println("tujuan :", tujuan)
// Tujuan lebih besar daripada sumber: hanya bagian awal yang terisi.
besar := make([]int, 5)
n2 := copy(besar, []int{90, 91})
fmt.Println("n2 tersalin :", n2)
fmt.Println("besar :", besar)
// copy pada area tujuan yang beririsan (overlap).
geser := []int{1, 2, 3, 4, 5}
copy(geser[1:], geser[:4])
fmt.Println("geser :", geser)
}