Golang Dasar: Dari Nol hingga CLI Production-Ready5/10

Bab 5 dari 10

Array, Slice, dan Map

Tujuan Pembelajaran

Setelah bab ini, Anda mampu:

  • membedakan array, slice, dan map;
  • mengelola panjang dan kapasitas slice;
  • memahami backing array, append, dan copy;
  • membedakan koleksi nil dan kosong;
  • memakai map dengan pola lookup comma-ok.

1. Array: Ukuran Bagian dari Tipe

Array menyimpan elemen berjumlah tetap. Panjangnya menjadi bagian dari tipe; [3]int berbeda dari [4]int.

Code
package main

import "fmt"

func main() {
	var nol [3]int
	prima := [4]int{2, 3, 5, 7}
	otomatis := [...]string{"merah", "hijau", "biru"}

	fmt.Println(nol, prima, otomatis)
	fmt.Println(len(prima), prima[0])
}

Assignment dan parameter array menyalin seluruh array. Array berguna untuk ukuran intrinsik dan tetap, misalnya hash 32 byte. Untuk koleksi umum, slice lebih fleksibel.

2. Slice: Tampilan ke Backing Array

Slice bukan array. Slice adalah descriptor yang mereferensikan segmen backing array, dengan panjang (len) dan kapasitas (cap).

Code
package main

import "fmt"

func main() {
	angka := []int{10, 20, 30, 40, 50}
	bagian := angka[1:4]
	fmt.Println(bagian, len(bagian), cap(bagian))

	bagian[0] = 99
	fmt.Println(angka)
}

Batas a[low:high] mencakup low dan tidak mencakup high. Kapasitas dihitung dari awal slice sampai akhir backing array yang tersedia.

Memuat diagram…

Buat slice dengan make ketika panjang atau kapasitas diketahui:

Code
package main

import "fmt"

func main() {
	a := make([]int, 3)
	b := make([]int, 0, 8)
	fmt.Println(a, len(a), cap(a))
	fmt.Println(b, len(b), cap(b))
}

make([]int, 3) menghasilkan tiga elemen yang sudah dapat diindeks. make([]int, 0, 8) belum memiliki elemen, sehingga b[0] panic; kapasitas bukan panjang.

3. append, Kapasitas, dan Alias

append mengembalikan slice baru dan hasilnya harus disimpan. Jika kapasitas cukup, backing array lama dapat dipakai; jika tidak, runtime mengalokasikan array baru.

Code
package main

import "fmt"

func main() {
	nilai := make([]int, 0, 2)
	nilai = append(nilai, 10, 20)
	nilai = append(nilai, 30)
	fmt.Println(nilai, len(nilai), cap(nilai))
}

Karena dua slice dapat berbagi backing array, perubahan atau append kadang terlihat melalui slice lain dan kadang tidak setelah realokasi. Jangan bergantung pada detail pertumbuhan kapasitas.

Gunakan full slice expression untuk membatasi kapasitas saat perlu mencegah append menimpa bagian lain:

Code
semua := []int{1, 2, 3, 4}
kiri := semua[:2:2]
kiri = append(kiri, 9)
fmt.Println(semua, kiri)

Kapasitas kiri dibatasi dua, sehingga append memerlukan backing array baru.

4. Menyalin Slice

Assignment slice menyalin header, bukan elemen. Untuk salinan independen, gunakan copy atau append ke slice baru.

Code
package main

import "fmt"

func main() {
	asal := []int{1, 2, 3}
	salinan := make([]int, len(asal))
	copy(salinan, asal)

	salinan[0] = 99
	fmt.Println(asal, salinan)
}

copy(dst, src) mengembalikan jumlah elemen yang disalin, yaitu minimum panjang keduanya.

5. Slice Nil dan Kosong

Code
var nilSlice []int
emptySlice := []int{}
madeEmpty := make([]int, 0)

Ketiganya memiliki panjang nol dan aman untuk range serta append. Hanya nilSlice == nil yang benar. Perbedaan dapat tampak pada serialisasi atau API eksternal (null versus []), sehingga ikuti kontrak boundary. Slice tidak dapat dibandingkan dengan slice lain; hanya dapat dibandingkan dengan nil.

6. Map dan comma-ok

Map menyimpan pasangan key-value. Key harus comparable; slice, map, dan function tidak dapat menjadi key.

Code
package main

import "fmt"

func main() {
	skor := map[string]int{
		"Ayu": 90,
		"Bima": 82,
	}

	skor["Citra"] = 88
	nilai, ada := skor["Ayu"]
	fmt.Println(nilai, ada)

	hapusNilai, ada := skor["Doni"]
	fmt.Println(hapusNilai, ada)

	delete(skor, "Bima")
	fmt.Println(len(skor))
}

Lookup key yang tidak ada menghasilkan zero value. Karena zero value mungkin nilai valid, gunakan bentuk nilai, ada := m[key] untuk membedakannya.

Map nil dapat dibaca dan menghasilkan zero value, tetapi penulisan menyebabkan panic:

Code
var m map[string]int
fmt.Println(m["x"])
m = make(map[string]int)
m["x"] = 1

Map adalah reference-like descriptor. Assignment atau pengiriman ke fungsi membuat descriptor baru yang mengacu data map sama. Map tidak aman untuk read/write bersamaan tanpa sinkronisasi. Urutan iterasinya tidak ditentukan.

Pola penghitung:

Code
package main

import "fmt"

func main() {
	frekuensi := make(map[rune]int)
	for _, r := range "banana" {
		frekuensi[r]++
	}
	fmt.Println(frekuensi['a'])
}

Zero value int membuat increment langsung bekerja tanpa pemeriksaan keberadaan.

Kesalahan Umum

  • Menganggap kapasitas sebagai elemen yang dapat diindeks. Indeks harus selalu kurang dari len.
  • Mengabaikan hasil append. Tulis s = append(s, x).
  • Mengira assignment slice menyalin data. Gunakan copy untuk independensi.
  • Tidak menyadari alias backing array. Salin atau batasi kapasitas ketika isolasi diperlukan.
  • Menulis ke map nil. Inisialisasi dengan literal atau make.
  • Mengandalkan zero value lookup untuk membuktikan key ada. Gunakan comma-ok.
  • Mengandalkan urutan iterasi map. Urutkan key untuk keluaran stabil.
  • Membandingkan dua slice atau map. Keduanya hanya dapat dibandingkan dengan nil.

Latihan

  1. Buat array tujuh suhu harian, lalu hitung rata-ratanya melalui slice.
  2. Buat slice dengan kapasitas awal lima, tambahkan sepuluh elemen, dan cetak len/cap setelah setiap append.
  3. Demonstrasikan dua slice yang berbagi backing array, lalu buat versi independen dengan copy.
  4. Hitung frekuensi setiap kata dalam kalimat memakai map[string]int.
  5. Buat fungsi lookup kontak yang mengembalikan nomor dan bool, termasuk kasus nomor kosong sebagai nilai valid.
  6. Bandingkan perilaku slice nil dan kosong saat len, range, append, dan pemeriksaan == nil.

Rangkuman

Array memiliki ukuran tetap yang menjadi bagian tipe. Slice menyediakan tampilan fleksibel ke backing array melalui pointer, panjang, dan kapasitas; append dapat mempertahankan atau mengganti storage, sedangkan copy membuat elemen independen. Map memberi lookup efisien tetapi tidak menjamin urutan. Pahami nilai nil, aliasing, batas indeks, dan pola comma-ok agar pengelolaan koleksi tetap benar.