Skip to content

Map Go

Umumnya implementasi struktur data map biasanya ada dua jenis hash table dan search tree perbedaannya adalah yang pertama tidak berurutan yang kedua berurutan. Di Go implementasi map didasarkan pada hash bucket (ini juga merupakan jenis hash table) jadi juga tidak berurutan bagian ini tidak akan menjelaskan terlalu banyak tentang prinsip implementasi ini melampaui ruang lingkup dasar selanjutnya akan dilakukan analisis mendalam.

TIP

Ingin memahami prinsip map dapat pergi ke implementasi map

Inisialisasi

Di Go kunci map harus dapat dibandingkan misalnya string int dapat dibandingkan sedangkan []int tidak dapat dibandingkan jadi tidak dapat sebagai kunci map. Ada dua metode untuk menginisialisasi map yang pertama adalah literal formatnya adalah sebagai berikut

go
map[keyType]valueType{}

Beberapa contoh

go
mp := map[int]string{
   0: "a",
   1: "a",
   2: "a",
   3: "a",
   4: "a",
}

mp := map[string]int{
   "a": 0,
   "b": 22,
   "c": 33,
}

Metode kedua adalah menggunakan fungsi built-in make untuk map而言 menerima dua parameter masing-masing adalah tipe dan kapasitas awal contohnya sebagai berikut

go
mp := make(map[string]int, 8)

mp := make(map[string][]int, 10)

Map adalah tipe referensi nilai nol atau map yang belum diinisialisasi dapat diakses tetapi tidak dapat menyimpan elemen jadi harus mengalokasikan memori untuknya.

go
func main() {
   var mp map[string]int
   mp["a"] = 1
   fmt.Println(mp)
}
panic: assignment to entry in nil map

TIP

Saat menginisialisasi map sebaiknya mengalokasikan kapasitas yang wajar untuk mengurangi jumlah ekspansi.

Akses

Cara mengakses map seperti mengakses array melalui indeks.

go
func main() {
  mp := map[string]int{
    "a": 0,
    "b": 1,
    "c": 2,
    "d": 3,
  }
  fmt.Println(mp["a"])
  fmt.Println(mp["b"])
  fmt.Println(mp["d"])
  fmt.Println(mp["f"])
}
0
1
3
0

Melalui kode dapat diamati meskipun map tidak ada pasangan kunci-nilai "f" tetapi tetap ada nilai pengembalian. Map untuk kunci yang tidak ada nilai pengembaliannya adalah nilai nol dari tipe yang sesuai dan saat mengakses map sebenarnya ada dua nilai pengembalian nilai pengembalian pertama adalah nilai dari tipe yang sesuai nilai pengembalian kedua adalah nilai boolean mewakili apakah kunci ada misalnya

go
func main() {
   mp := map[string]int{
      "a": 0,
      "b": 1,
      "c": 2,
      "d": 3,
   }
   if val, exist := mp["f"]; exist {
      fmt.Println(val)
   } else {
      fmt.Println("kunci tidak ada")
   }
}

Meminta panjang map

go
func main() {
   mp := map[string]int{
      "a": 0,
      "b": 1,
      "c": 2,
      "d": 3,
   }
   fmt.Println(len(mp))
}

Menyimpan Nilai

Cara menyimpan nilai map juga mirip seperti menyimpan nilai array misalnya

go
func main() {
   mp := make(map[string]int, 10)
   mp["a"] = 1
   mp["b"] = 2
   fmt.Println(mp)
}

Saat menyimpan nilai menggunakan kunci yang sudah ada akan menimpa nilai asli

go
func main() {
   mp := make(map[string]int, 10)
   mp["a"] = 1
   mp["b"] = 2
   if _, exist := mp["b"]; exist {
      mp["b"] = 3
   }
   fmt.Println(mp)
}

Tetapi juga ada situasi khusus yaitu saat kunci adalah math.NaN()

go
func main() {
  mp := make(map[float64]string, 10)
  mp[math.NaN()] = "a"
  mp[math.NaN()] = "b"
  mp[math.NaN()] = "c"
  _, exist := mp[math.NaN()]
  fmt.Println(exist)
  fmt.Println(mp)
}
false
map[NaN:c NaN:a NaN:b]

Melalui hasil dapat diamati kunci yang sama tidak menimpa malah dapat ada beberapa juga tidak dapat判断 apakah ada jadi tidak dapat mengambil nilai secara normal. Karena NaN adalah standar IEE754 yang didefinisikan implementasinya diselesaikan oleh instruksi assembly tingkat bawah UCOMISD ini adalah instruksi untuk membandingkan floating point presisi ganda tanpa urutan instruksi ini akan mempertimbangkan situasi NaN jadi hasilnya adalah angka apa pun tidak sama dengan NaN NaN juga tidak sama dengan dirinya sendiri ini juga menyebabkan setiap nilai hash tidak sama. Tentang hal ini komunitas juga pernah berdiskusi dengan sengajat tetapi resmi认为 tidak perlu memodifikasi jadi sebaiknya menghindari menggunakan NaN sebagai kunci map.

Menghapus

go
func delete(m map[Type]Type1, key Type)

Menghapus pasangan kunci-nilai memerlukan fungsi built-in delete misalnya

go
func main() {
   mp := map[string]int{
      "a": 0,
      "b": 1,
      "c": 2,
      "d": 3,
   }
   fmt.Println(mp)
   delete(mp, "a")
   fmt.Println(mp)
}
map[a:0 b:1 c:2 d:3]
map[b:1 c:2 d:3]

Perlu diperhatikan jika nilai adalah NaN bahkan tidak dapat menghapus pasangan kunci-nilai tersebut.

go
func main() {
   mp := make(map[float64]string, 10)
   mp[math.NaN()] = "a"
   mp[math.NaN()] = "b"
   mp[math.NaN()] = "c"
   fmt.Println(mp)
   delete(mp, math.NaN())
   fmt.Println(mp)
}
map[NaN:c NaN:a NaN:b]
map[NaN:c NaN:a NaN:b]

Iterasi

Melalui for range dapat melakukan iterasi pada map misalnya

go
func main() {
   mp := map[string]int{
      "a": 0,
      "b": 1,
      "c": 2,
      "d": 3,
   }
   for key, val := range mp {
      fmt.Println(key, val)
   }
}
c 2
d 3
a 0
b 1

Dapat dilihat hasilnya tidak berurutan juga membuktikan map disimpan secara tidak berurutan. Yang layak disebutkan adalah NaN meskipun tidak dapat diakses secara normal tetapi dapat diakses melalui iterasi misalnya

go
func main() {
   mp := make(map[float64]string, 10)
   mp[math.NaN()] = "a"
   mp[math.NaN()] = "b"
   mp[math.NaN()] = "c"
   for key, val := range mp {
      fmt.Println(key, val)
   }
}
NaN a
NaN c
NaN b

Mengosongkan

Sebelum go1.21 ingin mengosongkan map hanya dapat melakukan delete pada setiap kunci map

go
func main() {
  m := map[string]int{
    "a": 1,
    "b": 2,
  }
  for k, _ := range m {
    delete(m, k)
  }
  fmt.Println(m)
}

Tetapi go1.21 memperbarui fungsi clear tidak perlu lagi melakukan operasi sebelumnya hanya perlu satu clear dapat mengosongkan

go
func main() {
  m := map[string]int{
    "a": 1,
    "b": 2,
  }
  clear(m)
  fmt.Println(m)
}

Output

map[]

Set

Set adalah kumpulan yang tidak berurutan tidak berisi elemen duplikat Go tidak menyediakan implementasi struktur data serupa tetapi kunci map justru tidak berurutan dan tidak dapat duplikat jadi juga dapat menggunakan map untuk menggantikan set.

go
func main() {
  set := make(map[int]struct{}, 10)
  for i := 0; i < 10; i++ {
    set[rand.Intn(100)] = struct{}{}
  }
  fmt.Println(set)
}
map[0:{} 18:{} 25:{} 40:{} 47:{} 56:{} 59:{} 81:{} 87:{}]

TIP

Struct kosong tidak menempati memori

Perhatian

Map bukan struktur data yang aman untuk konkurensi tim Go认为 sebagian besar situasi penggunaan map tidak melibatkan skenario konkurensi tinggi memperkenalkan mutex lock akan sangat mengurangi performa map memiliki mekanisme deteksi baca-tulis internal jika konflik akan memicu fatal error. Misalnya situasi berikut sangat mungkin memicu fatal.

go
func main() {

   group.Add(10)
   // map
   mp := make(map[string]int, 10)
   for i := 0; i < 10; i++ {
      go func() {
         // Operasi tulis
         for i := 0; i < 100; i++ {
            mp["helloworld"] = 1
         }
         // Operasi baca
         for i := 0; i < 10; i++ {
            fmt.Println(mp["helloworld"])
         }
         group.Done()
      }()
   }
   group.Wait()
}
fatal error: concurrent map writes

Dalam situasi ini perlu menggunakan sync.Map untuk menggantikan.

Golang by www.golangdev.cn edit