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

go
angka = append(angka, 4, 5)           // dua elemen sekaligus
angka = append(angka, genap...)       // tempel seluruh isi slice genap

Operator ... 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:

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:

text
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:

  1. Baris 7–8 (angka = append(angka, 3)): satu elemen ditambahkan dan hasilnya direassign; setiap pemanggilan append di program ini memakai pola yang sama.
  2. Baris 12 (append(angka, 4, 5)): dua elemen masuk sekaligus dalam satu pemanggilan.
  3. Baris 16 (append(angka, genap...)): operator ... membuka isi genap sehingga angka bertambah dua elemen menjadi tujuh.
  4. Baris 21–23 (n := copy(tujuan, asal)): tujuan hanya berisi tiga slot, sehingga n bernilai 3 dan tujuan menampung [10 20 30] — elemen 40 dan 50 sumber tidak ikut.
  5. Baris 27–29 (n2 := copy(besar, ...)): sumber hanya dua elemen, jadi n2 bernilai 2; besar terisi [90 91 0 0 0] dan tiga slot sisanya tetap zero value.
  6. 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], dan copy menyelesaikannya 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 #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

append-copy-demo.go

Unduh berkas
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)
}