


Peta Go Dijelaskan: Cara Pasangan Nilai Kunci Sebenarnya Disimpan
Ini adalah petikan siaran; siaran penuh tersedia di sini: https://victoriametrics.com/blog/go-map/.
Jika anda baru menggunakan Go, agak mengelirukan untuk memikirkan cara menggunakan peta dalam Go. Dan walaupun anda lebih berpengalaman, memahami cara peta benar-benar berfungsi boleh menjadi agak sukar.
Ambil contoh ini: Pernahkah anda menetapkan 'petunjuk' untuk peta dan tertanya-tanya mengapa ia dipanggil 'petunjuk' dan bukan sesuatu yang mudah seperti panjang, seperti yang kita lakukan dengan kepingan?
// hint = 10 m := make(map[string]int, 10)
Atau mungkin anda perasan bahawa apabila anda menggunakan gelung jarak jauh pada peta, tertib itu tidak sepadan dengan tertib sisipan, malah ia berubah jika anda menggelung pada peta yang sama pada masa yang berbeza. Tetapi peliknya, jika anda mengulanginya pada masa yang sama, pesanan biasanya tetap sama.
Ini adalah cerita yang panjang, jadi kencangkan tali pinggang keledar anda dan selami.
Sebelum kita meneruskan, sekadar makluman—maklumat di sini adalah berdasarkan Go 1.23. Jika keadaan telah berubah dan ini tidak terkini, sila ping saya di X(@func25).
Peta dalam Go: Mula Pantas
Jadi, mari bercakap tentang peta dalam Go, ia adalah jenis terbina dalam yang bertindak sebagai storan nilai kunci. Tidak seperti tatasusunan yang anda terperangkap dengan kunci sebagai indeks yang semakin meningkat seperti 0, 1, 2 dan seterusnya, dengan peta, kuncinya boleh menjadi sebarang jenis yang setanding.
Itu memberikan anda lebih banyak fleksibiliti.
m := make(map[string]int) m["a"] = 1 m["b"] = 2 m // map[a:1 b:2]
Dalam contoh itu, kami mencipta peta kosong menggunakan make(), dengan kuncinya ialah rentetan dan nilainya ialah int.
Sekarang, bukannya memberikan setiap kunci secara manual, anda boleh menjimatkan masa anda dengan menggunakan literal peta. Ini membolehkan anda menyediakan pasangan nilai kunci anda sekaligus, tepat apabila anda membuat peta:
m := map[string]int{ "a": 1, "b": 2, }
Apa yang anda lakukan ialah menyenaraikan kunci dan nilainya di dalam pendakap kerinting apabila anda mula-mula mencipta peta, semudah itu.
Dan jika anda menyedari kemudian bahawa anda tidak memerlukan pasangan nilai kunci tertentu lagi, Go akan membantu anda. Terdapat fungsi padam yang berguna yang, baik, memadamkan kunci yang anda tidak mahu: padam(m, "a").
Nilai sifar peta ialah sifar dan peta sifar adalah seperti peta kosong dalam beberapa cara. Anda boleh cuba mencari kunci di dalamnya, dan Go tidak akan panik dan ranap program anda.
Jika anda mencari kunci yang tiada di sana, Go secara senyap-senyap memberikan anda "nilai sifar" untuk jenis nilai peta itu:
var m map[string]int println(m["a"]) // 0 m["a"] = 1 // panic: assignment to entry in nil map
Tetapi inilah perkaranya: anda tidak boleh menambah pasangan nilai kunci baharu pada peta sifar.
Malah, Go mengendalikan peta dengan cara yang hampir sama dengan cara ia menangani kepingan. Kedua-dua peta dan kepingan bermula sebagai sifar, dan Go tidak panik jika anda melakukan sesuatu yang "tidak berbahaya" dengan mereka semasa mereka tiada. Sebagai contoh, anda boleh melingkari sekeping nol tanpa sebarang "drama".
Jadi, apa yang berlaku jika anda cuba mengulangi peta kosong?
var m map[string]int for k, v := range m { println(k, v) }
Tiada apa-apa yang berlaku, tiada ralat, tiada kejutan. Ia secara senyap-senyap tidak melakukan apa-apa.
Pendekatan Go adalah untuk menganggap nilai lalai apa-apa jenis sebagai sesuatu yang berguna, bukan sesuatu yang menyebabkan program anda meledak. Satu-satunya masa Go membuat kesesuaian ialah apabila anda melakukan sesuatu yang benar-benar menyalahi undang-undang, seperti cuba menambah pasangan nilai kunci baharu pada peta sifar atau mengakses indeks di luar sempadan dalam kepingan.
Terdapat beberapa perkara lagi yang perlu anda ketahui tentang peta dalam Go:
- Gelung jarak jauh di atas peta tidak akan mengembalikan kekunci dalam sebarang susunan tertentu.
- Peta tidak selamat untuk benang, masa jalan Go akan menyebabkan ralat maut jika anda cuba membaca (atau mengulang dengan jarak jauh) dan menulis pada peta yang sama secara serentak.
- Anda boleh menyemak sama ada kunci berada dalam peta dengan melakukan semakan ok mudah: _, ok := m[kunci].
- Jenis kunci untuk peta mestilah setanding.
Mari kita menyelami perkara terakhir tentang kunci peta. Saya telah menyebut sebelum ini bahawa "kunci itu boleh menjadi apa-apa jenis yang setanding," tetapi terdapat lebih banyak daripada itu.
"Jadi, apakah sebenarnya jenis yang setanding? Dan apa yang tidak?"
Ia agak mudah: jika anda boleh menggunakan == untuk membandingkan dua nilai daripada jenis yang sama, maka jenis itu dianggap setanding.
func main() { var s map[int]string if s == s { println("comparable") } } // compile error: invalid operation: s == s (map can only be compared to nil)
Tetapi seperti yang anda lihat, kod di atas tidak dikompil. Pengkompil mengadu: "operasi tidak sah: s == s (peta hanya boleh dibandingkan dengan sifar)."
Peraturan yang sama ini digunakan untuk jenis lain yang tidak setanding seperti kepingan, fungsi atau struktur yang mengandungi kepingan atau peta, dsb. Jadi, jika anda cuba menggunakan mana-mana jenis tersebut sebagai kunci dalam peta, anda kurang beruntung.
func main() { var s map[[]int]string } // compile error: invalid map key type []intcompilerIncomparableMapKey
Tetapi inilah sedikit rahsia, antara muka boleh setanding dan tidak setanding.
Apakah maksudnya? Anda benar-benar boleh menentukan peta dengan antara muka kosong sebagai kunci tanpa sebarang ralat penyusunan. Tetapi berhati-hati, ada kemungkinan besar anda akan menghadapi ralat masa jalan.
func main() { m := map[interface{}]int{ 1: 1, "a": 2, } m[[]int{1, 2, 3}] = 3 m[func() {}] = 4 } // panic: runtime error: hash of unhashable type []int // panic: runtime error: hash of unhashable type func()
Everything looks fine until you try to assign an uncomparable type as a map key.
That's when you'll hit a runtime error, which is trickier to deal with than a compile-time error. Because of this, it's usually a good idea to avoid using interface{} as a map key unless you have a solid reason and constraints that prevent misuse.
But that error message: "hash of unhashable type []int" might seem a bit cryptic. What's this about a hash? Well, that's our cue to dig into how Go handles things under the hood.
Map Anatomy
When explaining the internals of something like a map, it's easy to get bogged down in the nitty-gritty details of the Go source code. But we're going to keep it light and simple so even those new to Go can follow along.
What you see as a single map in your Go code is actually an abstraction that hides the complex details of how the data is organized. In reality, a Go map is composed of many smaller units called "buckets."
type hmap struct { ... buckets unsafe.Pointer ... }
Look at Go source code above, a map contains a pointer that points to the bucket array.
This is why when you assign a map to a variable or pass it to a function, both the variable and the function's argument are sharing the same map pointer.
func changeMap(m2 map[string]int) { m2["hello"] = 2 } func main() { m1 := map[string]int{"hello": 1} changeMap(m1) println(m1["hello"]) // 2 }
But don't get it twisted, maps are pointers to the hmap under the hood, but they aren't reference types, nor are they passed by reference like a ref argument in C#, if you change the whole map m2, it won't reflect on the original map m1 in the caller.
func changeMap(m2 map[string]int) { m2 = map[string]int{"hello": 2} } func main() { m1 := map[string]int{"hello": 1} changeMap(m1) println(m1["hello"]) // 1 }
In Go, everything is passed by value. What's really happening is a bit different: when you pass the map m1 to the changeMap function, Go makes a copy of the *hmap structure. So, m1 in the main() and m2 in the changeMap() function are technically different pointers point to the same hmap.
For more on this topic, there's a great post by Dave Cheney titled There is no pass-by-reference in Go.
Each of these buckets can only hold up to 8 key-value pairs, as you can see in the image below.
The map above has 2 buckets, and len(map) is 6.
So, when you add a key-value pair to a map, Go doesn't just drop it in there randomly or sequentially. Instead, it places the pair into one of these buckets based on the key's hash value, which is determined by hash(key, seed).
Let's see the simplest assignment scenario in the image below, when we have an empty map, and assign a key-value pair "hello": 1 to it.
It starts by hashing "hello" to a number, then it takes that number and mods it by the number of buckets.
Since we only have one bucket here, any number mod 1 is 0, so it's going straight into bucket 0 and the same process happens when you add another key-value pair. It'll try to place it in bucket 0, and if the first slot's taken or has a different key, it'll move to the next slot in that bucket.
Take a look at the hash(key, seed), when you use a for-range loop over two maps with the same keys, you might notice that the keys come out in a different order:
func main() { a := map[string]int{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5, "f": 6} b := map[string]int{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5, "f": 6} for i := range a { print(i, " ") } println() for i := range b { print(i, " ") } } // Output: // a b c d e f // c d e f a b
How's that possible? Isn't the key "a" in map a and the key "a" in map b hashed the same way?
But here's the deal, while the hash function used for maps in Go is consistent across all maps with the same key type, the seed used by that hash function is different for each map instance. So, when you create a new map, Go generates a random seed just for that map.
In the example above, both a and b use the same hash function because their keys are string types, but each map has its own unique seed.
"Wait, a bucket has only 8 slots? What happens if the bucket gets full? Does it grow like a slice?"
Well, sort of. When the buckets start getting full, or even almost full, depending on the algorithm's definition of "full", the map will trigger a growth, which might double the number of main buckets.
But here's where it gets a bit more interesting.
Wenn ich „Haupt-Buckets“ sage, beziehe ich mich auf ein anderes Konzept: „Überlauf-Buckets“. Diese kommen ins Spiel, wenn es zu einer Situation mit vielen Kollisionen kommt. Stellen Sie sich vor, Sie haben 4 Buckets, aber einer davon ist aufgrund hoher Kollisionen vollständig mit 8 Schlüssel-Wert-Paaren gefüllt, während die anderen 3 Buckets leer bleiben.
Der vollständige Beitrag ist hier verfügbar: https://victoriametrics.com/blog/go-map/.
Atas ialah kandungan terperinci Peta Go Dijelaskan: Cara Pasangan Nilai Kunci Sebenarnya Disimpan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

Video Face Swap
Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas











Golang lebih baik daripada Python dari segi prestasi dan skalabiliti. 1) Ciri-ciri jenis kompilasi Golang dan model konkurensi yang cekap menjadikannya berfungsi dengan baik dalam senario konvensional yang tinggi. 2) Python, sebagai bahasa yang ditafsirkan, melaksanakan perlahan -lahan, tetapi dapat mengoptimumkan prestasi melalui alat seperti Cython.

Golang lebih baik daripada C dalam kesesuaian, manakala C lebih baik daripada Golang dalam kelajuan mentah. 1) Golang mencapai kesesuaian yang cekap melalui goroutine dan saluran, yang sesuai untuk mengendalikan sejumlah besar tugas serentak. 2) C Melalui pengoptimuman pengkompil dan perpustakaan standard, ia menyediakan prestasi tinggi yang dekat dengan perkakasan, sesuai untuk aplikasi yang memerlukan pengoptimuman yang melampau.

GoisidealforbeginnersandSuekableforcloudandnetworkservicesduetoitssimplicity, kecekapan, danconcurrencyfeatures.1) installgofromtheofficialwebsiteandverifywith'goversion'.2)

Golang sesuai untuk pembangunan pesat dan senario serentak, dan C sesuai untuk senario di mana prestasi ekstrem dan kawalan peringkat rendah diperlukan. 1) Golang meningkatkan prestasi melalui pengumpulan sampah dan mekanisme konvensional, dan sesuai untuk pembangunan perkhidmatan web yang tinggi. 2) C mencapai prestasi muktamad melalui pengurusan memori manual dan pengoptimuman pengkompil, dan sesuai untuk pembangunan sistem tertanam.

Golang dan Python masing -masing mempunyai kelebihan mereka sendiri: Golang sesuai untuk prestasi tinggi dan pengaturcaraan serentak, sementara Python sesuai untuk sains data dan pembangunan web. Golang terkenal dengan model keserasiannya dan prestasi yang cekap, sementara Python terkenal dengan sintaks ringkas dan ekosistem perpustakaan yang kaya.

Perbezaan prestasi antara Golang dan C terutamanya ditunjukkan dalam pengurusan ingatan, pengoptimuman kompilasi dan kecekapan runtime. 1) Mekanisme pengumpulan sampah Golang adalah mudah tetapi boleh menjejaskan prestasi, 2) Pengurusan memori manual C dan pengoptimuman pengkompil lebih cekap dalam pengkomputeran rekursif.

Golang dan C masing-masing mempunyai kelebihan sendiri dalam pertandingan prestasi: 1) Golang sesuai untuk kesesuaian tinggi dan perkembangan pesat, dan 2) C menyediakan prestasi yang lebih tinggi dan kawalan halus. Pemilihan harus berdasarkan keperluan projek dan tumpukan teknologi pasukan.

Golangisidealforbuildingscalablesystemsduetoitseficiencyandcurrency, whilepythonexcelsinquickscriptinganddataanalysisduetoitssimplicityandvastecosystem.golang'sdesignencouragescouragescouragescouragescourageSlean, readablecodeanditsouragescouragescourscean,
