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
map[keyType]valueType{}Beberapa contoh
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
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.
func main() {
var mp map[string]int
mp["a"] = 1
fmt.Println(mp)
}panic: assignment to entry in nil mapTIP
Saat menginisialisasi map sebaiknya mengalokasikan kapasitas yang wajar untuk mengurangi jumlah ekspansi.
Akses
Cara mengakses map seperti mengakses array melalui indeks.
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
0Melalui 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
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
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
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
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()
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
func delete(m map[Type]Type1, key Type)Menghapus pasangan kunci-nilai memerlukan fungsi built-in delete misalnya
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.
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
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 1Dapat 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
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 bMengosongkan
Sebelum go1.21 ingin mengosongkan map hanya dapat melakukan delete pada setiap kunci map
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
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.
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.
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 writesDalam situasi ini perlu menggunakan sync.Map untuk menggantikan.
