summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--_content/blog/index.adoc3
-rw-r--r--_content/blog/maps/index.adoc354
2 files changed, 357 insertions, 0 deletions
diff --git a/_content/blog/index.adoc b/_content/blog/index.adoc
index 02ab166..3f2c122 100644
--- a/_content/blog/index.adoc
+++ b/_content/blog/index.adoc
@@ -89,6 +89,9 @@
* link:/blog/race-detector[Memperkenalkan pendeteksi _data race_^],
26 Juni 2013. Dmitry Vyukov dan Andrew Gerrand.
+* link:/blog/maps/[Go map^],
+ 6 Februari 2013. Andrew Gerrand.
+
=== 2012
* link:/blog/organizing-go-code/[Mengorganisasi kode Go^],
diff --git a/_content/blog/maps/index.adoc b/_content/blog/maps/index.adoc
new file mode 100644
index 0000000..dc834b0
--- /dev/null
+++ b/_content/blog/maps/index.adoc
@@ -0,0 +1,354 @@
+= Go map
+Andrew Gerrand
+6 Februari 2013
+:toc:
+
+== Pendahuluan
+
+Salah satu struktur data paling berguna dalam ilmu komputer adalah tabel
+_hash_.
+Kebanyakan implementasi tabel _hash_ memiliki banyak properti, tetapi secara
+umum mereka memiliki kecepatan pencarian, penambahan, dan penghapusan.
+Go menyediakan tipe map bawaan yang mengimplementasikan sebuah tabel _hash_.
+
+== Deklarasi dan inisiasi
+
+Sebuah tipe map pada Go bentuknya seperti berikut:
+
+----
+map[TipeKey]TipeNilai
+----
+
+yang mana `TipeKey` bisa tipe apa saja yang dapat
+link:/ref/spec#Comparison_operators[dibandingkan^]
+(lebih lanjut akan kita bahas nanti), dan `TipeNilai` yang bisa bertipe apa
+pun, termasuk map juga!
+
+Variabel `m` berikut adalah sebuah map dengan kunci bertipe `string` dan
+nilai bertipe `int`:
+
+----
+var m map[string]int
+----
+
+Tipe map adalah tipe referensi, seperti pointer atau slice, sehingga nilai
+dari variabel `m` di atas adalah `nil`;
+ia tidak menunjuk ke map yang telah diinisiasi.
+Sebuah map yang `nil` bersifat seperti map kosong saat pembacaan, namun
+mencoba menulis ke sebuah map yang `nil` akan menyebabkan _panic_;
+maka dari itu jangan pernah melakukan penulisan ke map yang belum diinisiasi.
+Untuk menginisiasi map, gunakan fungsi bawaan `make`:
+
+----
+m = make(map[string]int)
+----
+
+Fungsi `make` membuat alokasi dan menginisiasi sebuah struktur data map _hash_
+dan mengembalikan sebuah nilai map yang menunjuk ke _hash_ tersebut.
+Spesifikasi dari struktur data tersebut adalah detail implementasi dari
+_runtime_.
+Dalam artikel ini kita akan fokus pada penggunaan map, bukan implementasinya.
+
+
+== Menggunakan map
+
+Go menyediakan sintaksis umum untuk bekerja dengan map.
+Perintah berikut men-set kunci "route" untuk nilai `66`:
+
+----
+m["route"] = 66
+----
+
+Perintah berikut mengambil nilai yang disimpan dengan kunci "route" dan
+menyimpannya dalam sebuah variabel baru:
+
+----
+i := m["route"]
+----
+
+Jika kunci tidak ada, kita akan mendapatkan sebuah nilai kosong.
+Pada kasus ini secara tipe dari nilai adalah `int`, maka nilai kosongnya
+adalah 0:
+
+----
+j := m["root"]
+// j == 0
+----
+
+Fungsi bawaan `len` mengembalikan jumlah item dalam sebuah map:
+
+----
+n := len(m)
+----
+
+Fungsi bawaan `delete` menghapus sebuah item dalam map:
+
+----
+delete(m, "route")
+----
+
+Fungsi `delete` tidak mengembalikan nilai, dan akan tetap sukses bila kunci
+yang diberikan tidak ada dalam map.
+
+Penempatan dua-nilai memeriksa keberadaan dari sebuah kunci:
+
+----
+i, ok := m["route"]
+----
+
+Pada perintah tersebut, nilai yang pertama (`i`) diisi dengan nilai yang
+disimpan dalam kunci "route".
+Jika kunci tersebut tidak ada, maka nilai `i` akan kosong (0).
+Nilai yang kedua (`ok`) adalah sebuah tipe `bool` yang akan `true` jika kunci
+ada dalam map, dan `false` jika tidak ada.
+
+Untuk memeriksa sebuah kunci ada atau tidak tanpa mengambil nilainya, gunakan
+karakter garis-bawah pada nilai pertama:
+
+----
+_, ok := m["route"]
+----
+
+Untuk mengiterasi isi dari sebuah map, gunakan `range`:
+
+----
+for key, value := range m {
+ fmt.Println("Key:", key, "Value:", value)
+}
+----
+
+Untuk menginisiasi map dengan beberapa data, gunakan literal map:
+
+----
+commits := map[string]int{
+ "rsc": 3711,
+ "r": 2138,
+ "gri": 1908,
+ "adg": 912,
+}
+----
+
+Sintaksis yang sama dapat digunakan untuk menginisiasi map kosong, yang secara
+fungsionalitas identik dengan fungsi `make`:
+
+----
+m = map[string]int{}
+----
+
+
+== Eksploitasi nilai kosong
+
+Telah kita ketahui bahwa pembacaan sebuah nilai dengan kunci yang tidak ada
+pada map akan mengembalikan sebuah nilai kosong.
+
+Misalnya, sebuah map dengan nilai boolean dapat digunakan untuk struktur data
+_set_ (ingatlah bahwa nilai kosong dari tipe boolean adalah `false`).
+Contoh berikut mengiterasi _linked list_ dari `Node` dan mencetak nilainya.
+Ia menggunakan sebuah map dengan kunci berupa pointer ke Node untuk memeriksa
+apakah Node pernah dikunjungi dari dalam daftar.
+
+----
+type Node struct {
+ Next *Node
+ Value interface{}
+}
+var first *Node
+
+visited := make(map[*Node]bool)
+for n := first; n != nil; n = n.Next {
+ if visited[n] {
+ fmt.Println("cycle detected")
+ break
+ }
+ visited[n] = true
+ fmt.Println(n.Value)
+}
+----
+
+Ekspresi `visited[n]` bernilai `true` jika `n` pernah dikunjungi, atau `false`
+jika `n` tidak ada.
+Tidak perlu menggunakan bentuk penempatan dua-nilai untuk memeriksa keberadaan
+`n` dalam map;
+nilai kosong bawaan dari tipe `bool` telah melakukan hal tersebut.
+
+Contoh penggunaan nilai kosong lainnya yaitu map dari slice.
+Menambahkan sebuah item ke dalam slice yang nil akan mengalokasikan slice yang
+baru, sehingga cukup satu baris untuk menambahkan sebuah nilai ke dalam sebuah
+map dari slice;
+tidak perlu memeriksa apakah kunci ada atau tidak.
+Pada contoh berikut, variabel slice `people` diisi dengan nilai `Person`.
+Setiap `Person` memiliki `Name` dan slice `Likes`.
+Contoh ini membuat sebuah map untuk mengasosiasikan setiap _like_ dengan
+daftar orang yang menyukainya.
+
+----
+type Person struct {
+ Name string
+ Likes []string
+}
+var people []*Person
+
+likes := make(map[string][]*Person)
+for _, p := range people {
+ for _, l := range p.Likes {
+ likes[l] = append(likes[l], p)
+ }
+}
+----
+
+Untuk mencetak daftar orang yang menyukai "cheese":
+
+----
+for _, p := range likes["cheese"] {
+ fmt.Println(p.Name, "likes cheese.")
+}
+----
+
+Untuk mencetak jumlah orang yang menyukai "bacon":
+
+----
+fmt.Println(len(likes["bacon"]), "people like bacon.")
+----
+
+Ingat lah bahwa secara `range` dan `len` menganggap slice yang nil sebagai
+slice dengan panjang 0, kedua contoh tersebut akan berjalan walaupun tidak ada
+orang (dalam variabel `people`) yang menyukai "cheese" atau "bacon".
+
+
+== Tipe-tipe kunci dari map
+
+Seperti yang dibahas sebelumnya, kunci dari map bisa berupa tipe apa pun yang
+dapat dibandingkan.
+link:/ref/spec#Comparison_operators[Spesifikasi bahasa^]
+mendefinisikan hal ini lebih jelas, namun singkatnya, tipe-tipe yang dapat
+dibandingkan yaitu boolean, numeric, string, pointer, channel, interface, dan
+struct atau array yang berisi hanya tipe tersebut.
+Berarti tipe yang tidak dapat dibandingkan yaitu slice, map, dan fungsi;
+tipe-tipe tersebut tidak dapat dibandingkan lewat operator `==`, dan tidak
+bisa digunakan sebagai kunci dari map.
+
+Kalau tipe seperti string, int, dan tipe dasar lainnya cukup jelas kenapa bisa
+digunakan sebagai kunci dari map, namun yang mungkin kurang jelas adalah
+penggunakan struct sebagai kunci.
+Struct dapat digunakan sebagai kunci data dengan banyak dimensi.
+Contohnya, map dari map berikut dapat digunakan untuk menghitung kunjungan
+halaman web berdasarkan negara:
+
+----
+hits := make(map[string]map[string]int)
+----
+
+Map tersebut yaitu map dari string ke (map dari string ke int).
+Kunci dari map bagian luar adalah path ke sebuah halaman web dengan nilainya
+adalah sebuah map sendiri.
+Kunci dari map bagian dalam adalah dua-huruf kode negara dengan nilai dari map
+yaitu jumlah kunjungan.
+Ekspresi berikut mengambil jumlah kunjungan halaman "/doc" dari negara
+Australia:
+
+----
+n := hits["/doc/"]["au"]
+----
+
+Sayangnya, pendekatan seperti ini menjadi sukar pada saat menambah data,
+untuk setiap kunci bagian luar, kita harus memeriksa apakah map bagian dalam
+telah diinisiasi atau belum, dan menginisiasi-nya bila diperlukan:
+
+----
+func add(m map[string]map[string]int, path, country string) {
+ mm, ok := m[path]
+ if !ok {
+ mm = make(map[string]int)
+ m[path] = mm
+ }
+ mm[country]++
+}
+add(hits, "/doc/", "au")
+----
+
+Di sisi lain, pendekatan dengan menggunakan struct sebagai kunci mempermudah
+semua hal tersebut:
+
+----
+type Key struct {
+ Path, Country string
+}
+
+hits := make(map[Key]int)
+----
+
+Saat seseorang dari Vietnam mengunjungi halaman depan ("/"), meningkatkan
+nilai (dan juga membuat nilai baru) penghitung menjadi satu-baris saja:
+
+----
+hits[Key{"/", "vn"}]++
+----
+
+Begitu juga, cukup mudah untuk melihat berapa banyak orang dari Swiss yang
+telah membaca halaman spesifikasi ("/ref/spec"):
+
+----
+n := hits[Key{"/ref/spec", "ch"}]
+----
+
+
+== Konkurensi
+
+link:/doc/faq#atomic_maps[Map tidak aman digunakan secara konkuren^]:
+tidak didefinisikan apa yang akan terjadi bila kita membaca dan menulis pada
+map yang sama secara simultan.
+Jika kita harus membaca dan menulis ke sebuah map dari goroutine yang berbeda,
+akses ke map tersebut harus di-mediasi oleh sebuah mekanisme sinkronisasi.
+Salah satu cara umum untuk melindungi map yaitu dengan
+https://golang.org/pkg/sync/#RWMutex[sync.RWMutex^].
+
+Perintah berikut mendeklarasikan sebuah variabel `counter` bertipe struct
+anonim yang berisi sebuah map dan menanam `sync.RWMutex`.
+
+----
+var counter = struct{
+ sync.RWMutex
+ m map[string]int
+}{m: make(map[string]int)}
+----
+
+Untuk membaca dari nilai map dari `counter`, gunakan pengunci baca:
+
+----
+counter.RLock()
+n := counter.m["some_key"]
+counter.RUnlock()
+fmt.Println("some_key:", n)
+----
+
+Untuk menulis ke `counter`, gunakan pengunci tulis:
+
+----
+counter.Lock()
+counter.m["some_key"]++
+counter.Unlock()
+----
+
+
+== Urutan iterasi
+
+Saat mengiterasi sebuah map lewat pengulangan `range`, urutan iterasi tidak
+menentu dan tidak dijamin sama dari satu iterasi ke iterasi selanjutnya.
+Jika Anda membutuhkan iterasi yang stabil, Anda harus menyimpan sebuah
+struktur data terpisah yang menentukan urutan kunci.
+Contoh berikut menggunakan slice sebagai urutan kunci untuk mencetak sebuah
+`map[int]string` secara terurut:
+
+----
+import "sort"
+
+var m map[int]string
+var keys []int
+for k := range m {
+ keys = append(keys, k)
+}
+sort.Ints(keys)
+for _, k := range keys {
+ fmt.Println("Key:", k, "Value:", m[k])
+}
+----