Go には、map と呼ばれるハッシュ テーブルを実装するネイティブ型が組み込まれています。これは、一意のキーのコレクションと、それらの各キーの値のコレクションで構成されるデータ型です。
これは、たとえば、キーと値のペアを格納する他の言語の辞書と比較できます。これらの値には、前の投稿で見た配列やスライスと同じ方法で、キーを使用してアクセスします。
インデックスは配列やスライスのように数値に制限されず、要素は順序付けされていないため、マップを印刷すると、その印刷をオーバーライドして目的の順序を強制する限り、ランダムな順序が返されます。
マップを宣言するには、map[key]value を使用して行います。ここで、key はキーにしたい型になります (同等の型である必要があります https://go.dev/ref/spec#Comparison_operators ) と value は、int から構造体、または別のマップまで、必要に応じて、どのような型であっても、各キーにマップを格納したい型になります。
スライスと同様、マップは参照型です。つまり、マップのゼロ値は nil になります。
これは、その下にキーと値を格納するハッシュ テーブルがあり、それらは単にそれらのエンベロープ、抽象化であるために発生します。
次のように宣言すると:
var m map[int]int
その値はゼロになります。
値をゼロにしたい場合は、次の宣言を使用できます。
m := map[int]int{}
さらに、make 関数を使用して、スライスと同じように初期化することもできます。
m := make(map[string]string)
これを実行すると、ハッシュ マップが適切なメモリ プールで初期化され、そのデータ構造を指すマップが返されます。
マップへの値の追加は、配列やスライスと同様に、中括弧 [] と中括弧を使用して行われます。この例では、名前と年齢を保存するために、キーが文字列、値が整数のマップを作成します。
ages := make(map[string]int) ages["John"] = 33 ages["Charly"] = 27 ages["Jenny"] = 45 ages["Lisa"] = 19
マップを宣言するときに値を追加したい場合は、短い宣言を使用して、すべてを同じステップで実行できます。
ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19}
値を読み取るには、マップにキーを指定するだけで、その値が返されます。たとえば、リサの年齢を調べるには、次のようにします:
fmt.Println(ages["Lisa"]) // 19
存在しないキーにアクセスしようとすると、取得される値はその型のゼロ値になります。この場合は文字列であるため、"" になります。
マップ内に要素が存在するかどうかを確認するには、タイプがデフォルトであるかどうかを確認できますが、おそらく存在してもその値が空の文字列または int の場合は 0 であるため、あまり信頼性がありません。これはゼロ値と一致するため、Go は次の点で役立ちます。
val, ok := ages["Randy"]
マップを 2 つの値に等しい場合、最初の値はキーを介してアクセスされる要素の値 (この場合は存在しない「Randy」) になり、2 番目の値はブール値になり、それが存在するかどうかを示します。存在するかどうか。
値に興味がなく、単にキーの存在を確認したい場合は、次のように _ を使用して値を無視できます。
_, ok := ages["Randy"]
配列やスライスと同様に、len 関数を使用してマップ内に要素がいくつあるかを確認できます。
fmt.Println(len(ages)) // 4
値を変更したい場合は、キーを使用してその値にアクセスし、それを別の値と照合するだけで変更されます。
最初のマップを指す 2 番目のマップを宣言した場合、2 番目の値を変更すると、それは参照型であるため、最初の値も変更することになります。これは、どちらも下位にある同じハッシュ テーブルを共有するためです。
ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19} agesNew := ages agesNew["Bryan"] = 77 fmt.Println(agesNew) // map[Bryan:77 Charly:27 Jenny:45 John:33 Lisa:19] fmt.Println(ages) // map[Bryan:77 Charly:27 Jenny:45 John:33 Lisa:19]
マップから要素を削除するために、Go は削除するマップとキーを受け取る次のシグネチャ delete(m map[Type]Type1, key Type) を持つ削除関数を提供します。
前のケースでは、「Lisa」を削除したい場合は、次のように実行します。
delete(ages, "Lisa")
マップのコンテンツを調べたい場合は、配列とスライスに関する投稿ですでに見た範囲のバリエーションを備えた for を使用して実行できます。
その場合、最初の要素はインデックス、つまりキーとなり、2 番目の要素は値になります。
for key, value := range ages { fmt.Printf("%s: %d\n", key, value) } // Output: // Jenny: 45 // Lisa: 19 // John: 33 // Charly: 27
配列やスライスと同様、キーなしで値のみに興味がある場合は、_ を使用して省略できます。
for _, value := range ages { fmt.Println(value) } // Output: // 19 // 33 // 27 // 45
そして、興味のあるものが単にキーである場合は、範囲を 1 つの変数に代入してそれを取得できます。
for key := range ages { fmt.Println(key) } // Output: // John // Charly // Jenny // Lisa
As I mentioned in the introduction, in a map the information is not ordered, so when looping through it we cannot specify what order it follows, nor can Go guarantee that the order between executions is the same.
As we saw with arrays and slices, in the standard library there is a sort package which helps us sort elements: https://pkg.go.dev/sort
Following our example with ages and using sort, we can sort the keys of the map before traversing it and thus guarantee that it will be accessed in order.
ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19} keys := make([]string, 0, len(ages)) for k := range ages { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { fmt.Println(k, ages[k]) } // Output: // Charly 27 // Jenny 45 // John 33 // Lisa 19
We declare our ages map with the short declaration as we saw before.
We create a string slices to store the keys and use the make method with 0 length, since we do not have any keys at the moment, but we do reserve the capacity it will have using the len method for the length of our map.
We go through the ages map to keep its keys and add them to the created slice.
We sort the keys alphabetically with the sort.Strings function.
We go through the slice of keys, already ordered, and access the map with the key in question.
This way we will access the map in an orderly manner and we can do the logic that our program needs.
Something to keep in mind with maps is that they are not safe to use concurrently. If these are concurrent reads, either accessing a value or through a for with a range, there is no problem with multiple goroutines accessing it at the same time.
The problematic case is when you want to update the value of a map concurrently, either by adding or removing elements from it, and at the same time you are reading it from another side, for example.
To solve this situation there are several possible solutions, which I will not go into much detail, I will simply mention and leave it to your choice to delve deeper into them.
If we use the sync package: https://pkg.go.dev/sync from the standard library, we can control the synchrony between the different goroutines.
A possible use is the RWMutex type which allows us to lock and unlock reads and writes to a type. So if we have a type that contains a sync.RWMutex and a map we can control when it can be accessed.
Another interesting type to investigate within the same sync package is Map, which already offers us a series of functions that will help us work with our map, which in the end we will not be able to work with natively, as with the previous solution.
Depending on the use case we are implementing, one or the other will be more useful to us, and there is no one better than the other, it will always depend on what we need.
I hope everything that I have tried to explain in this post has been clear, and please if there is any part that has not been completely clear or there are parts that I have not covered that you would like me to do, leave me a comment right here or through my social networks that you have on my profile and I will be happy to respond.
以上がGoの地図の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。