Peta

Pembuatan peta, zero value kunci yang tidak ada, dan idiom comma-ok.

Daftar isi

Peta adalah struktur data kunci-nilai yang mengambil nilai berdasarkan kuncinya tanpa harus menjelajah isinya satu per satu. Topik ini membahas pembuatan peta lewat literal dan make, perilaku zero value ketika kunci yang dicari tidak ada, serta idiom comma-ok yang membedakan kunci yang benar-benar tidak ada dari kunci yang nilainya memang nol. Contoh di sini menggunakan catatan stok gudang dan penghitungan frekuensi piket.

Membuat dan mengisi peta#

Tipe peta ditulis map[K]V, yaitu tipe kunci diikuti tipe nilainya, misalnya map[string]int untuk kunci kode barang yang memetakan ke stok. Tipe kunci ditetapkan sekali pada deklarasi, sehingga seluruh kunci harus bertipe sama, demikian pula seluruh nilainya. Cara membuatnya ada dua, yaitu literal map[string]int{...} yang memasangkan kunci dan nilai sekaligus, dan make(map[string]int) yang menghasilkan peta kosong siap diisi. Entri baru ditambahkan lewat penugasan berindeks kunci, sedangkan penugasan pada kunci yang sudah ada menimpa nilainya:

go peta-literal.go
package main

import "fmt"

func main() {
	stok := map[string]int{
		"kertas-a4":   120,
		"tinta-hitam": 45,
		"penghapus":   80,
	}

	fmt.Println("Isi peta        :", stok)
	fmt.Println("Stok kertas-a4  :", stok["kertas-a4"])

	stok["spidol"] = 25
	fmt.Println("Isi setelah tambah:", stok)
}
text Keluaran
Isi peta        : map[kertas-a4:120 penghapus:80 tinta-hitam:45]
Stok kertas-a4  : 120
Isi setelah tambah: map[kertas-a4:120 penghapus:80 spidol:25 tinta-hitam:45]

Fungsi len juga berlaku untuk peta dan menghitung banyak entrinya. Bentuk make(map[string]int, 100) menghasilkan peta kosong dengan petunjuk kapasitas untuk seratus entri, sehingga pengisian bervolume besar tidak perlu membangun ulang struktur internalnya berkali-kali. Petunjuk ini bukan batas, karena peta tetap tumbuh melewatinya, melainkan penyiapan awal yang menekan pemindahan data.

Variabel peta yang dideklarasikan tanpa nilai awal berisi peta nil, yaitu peta tanpa entri apa pun. Membaca dari peta nil aman dan menghasilkan zero value tipe nilainya, tetapi menulis ke peta nil menghentikan program. Penyiapan dengan make atau literal adalah langkah wajib sebelum penugasan kunci pertama:

go Penugasan pada peta nil yang berujung panic
// tidak-dijalankan
var m map[string]int
m["kertas-a4"] = 10 // panic: assignment to entry in nil map

Kunci yang tidak ada menghasilkan zero value#

Pembacaan kunci yang tidak terdaftar tidak memicu panic. Go mengembalikan zero value dari tipe nilainya, yaitu 0 untuk int, string kosong untuk string, dan false untuk bool:

go kunci-tidak-ada.go
package main

import "fmt"

func main() {
	stok := map[string]int{"kertas-a4": 120}

	fmt.Println("kunci ada  :", stok["kertas-a4"])
	fmt.Println("kunci tidak:", stok["penggaris"])
}
text Keluaran
kunci ada  : 120
kunci tidak: 0

Perilaku ini bukan celah, melainkan desain yang sengaja. Zero value yang dikembalikan adalah milik tipe nilai peta, bukan milik peta itu sendiri, sehingga peta map[string]string menghasilkan string kosong pada kunci yang hilang. Karena kunci yang tidak ada sudah menghasilkan nol, penghitungan frekuensi tidak perlu memeriksa keberadaan kunci lebih dahulu. Nilai baru cukup ditambahkan langsung pada hasil pembacaannya:

go frekuensi.go
package main

import "fmt"

func main() {
	jumlah := map[string]int{}

	for _, nama := range []string{"Ali", "Budi", "Ali", "Citra", "Ali"} {
		jumlah[nama]++
	}

	fmt.Println("Frekuensi piket:", jumlah)
}
text Keluaran
Frekuensi piket: map[Ali:3 Budi:1 Citra:1]

Ekspresi jumlah[nama]++ membaca zero value 0 ketika nama belum terdaftar, lalu menulis kembali hasilnya dengan satu ditambahkan. Pola ini bekerja untuk tipe nilai apa pun yang zero value-nya cocok dijadikan titik awal, misalnya int untuk penghitungan atau float64 untuk penjumlahan. Apabila titik awalnya bukan nol, misalnya mencari nilai terbesar, pemeriksaan keberadaan kunci tetap diperlukan. Pola penghitung semacam ini lazim dijumpai pada kode Go dan menjadi salah satu penerapan langsung dari prinsip nilai nol.

Idiom comma-ok#

Zero value menyisakan satu pertanyaan yang tidak terjawab, yaitu apakah kunci benar-benar ada atau nilainya memang nol. Untuk stok, angka nol pada kunci yang terdaftar dan angka nol pada kunci yang tidak ada bermakna berbeda. Go menjawabnya dengan bentuk dua hasil yang dikenal sebagai idiom comma-ok (comma, ok), dinamai demikian karena kedua hasilnya dipisahkan koma:

go comma-ok.go
package main

import "fmt"

func main() {
	stok := map[string]int{"kertas-a4": 120, "spidol": 0}

	if n, ok := stok["spidol"]; ok {
		fmt.Println("spidol terdaftar, stoknya:", n)
	}

	if n, ok := stok["penggaris"]; !ok {
		fmt.Println("penggaris tidak terdaftar, angkanya:", n)
	}
}
text Keluaran
spidol terdaftar, stoknya: 0
penggaris tidak terdaftar, angkanya: 0

Hasil kedua bernama ok bernilai true hanya ketika kunci terdaftar, tanpa memandang nilainya nol atau bukan. Pemeriksaan yang sama bekerja pada peta nil, sehingga pembacaan peta yang belum dibuat tidak memerlukan cabang khusus. Bentuk if n, ok := stok[...]; ok menggabungkan pembacaan dan pemeriksaan dalam satu pernyataan, sehingga kedua variabelnya terbatas pada blok pemeriksaan tersebut. Ketika nilai nol tidak membawa makna khusus, bentuk satu hasil tetap sah dan lebih ringkas, sedangkan bentuk _, ok := stok[k] dipakai ketika nilainya tidak dibutuhkan sama sekali.

Kesalahan umum#

  1. Menulis ke peta nil. Peta yang baru dideklarasikan dengan var m map[string]int belum memiliki array internal, sehingga penugasan kunci pertamanya memicu panic. Perbaikannya, buat peta dengan make atau literal sebelum mengisinya.
  2. Membaca tanpa comma-ok ketika nol ambigu. Pembacaan satu hasil membuat kunci yang hilang dan kunci yang bernilai nol terlihat sama. Perbaikannya, gunakan bentuk dua hasil setiap kali keberadaan kunci memengaruhi keputusan program.
  3. Memakai kunci yang tidak dapat dibandingkan. Kunci peta harus bertipe yang mendukung perbandingan ==, seperti string, bilangan, dan bool, sedangkan slice, peta, dan fungsi ditolak kompiler. Perbaikannya, gunakan tipe kunci yang sebanding, misalnya menggabungkan beberapa bagian kunci menjadi satu string.

Ringkasan#

  • Peta map[K]V memetakan kunci unik ke nilai, dan dibuat lewat literal atau make.
  • Kunci yang tidak ada menghasilkan zero value tipe nilainya, sehingga pola penghitung m[k]++ dapat ditulis tanpa pemeriksaan keberadaan.
  • Idiom comma-ok v, ok := m[k] membedakan kunci yang tidak ada dari kunci yang nilainya memang nol.
  • Peta nil aman dibaca tetapi menghentikan program saat ditulis, sehingga make wajib dilakukan sebelum pengisian.

Referensi#