「労働者が自分の仕事をうまくやりたいなら、まず自分の道具を研ぎ澄まさなければなりません。」 - 孔子、「論語。陸霊公」
表紙 > プログラミング > Goの地図

Goの地図

2024 年 8 月 24 日に公開
ブラウズ:317

Maps in Go

イントロ

Go には、map と呼ばれるハッシュ テーブルを実装するネイティブ型が組み込まれています。これは、一意のキーのコレクションと、それらの各キーの値のコレクションで構成されるデータ型です。
これは、たとえば、キーと値のペアを格納する他の言語の辞書と比較できます。これらの値には、前の投稿で見た配列やスライスと同じ方法で、キーを使用してアクセスします。
インデックスは配列やスライスのように数値に制限されず、要素は順序付けされていないため、マップを印刷すると、その印刷をオーバーライドして目的の順序を強制するための何も行わなければ、ランダムな順序が返されます。

マップの宣言と初期化

マップを宣言するには、map[key]value を使用して行います。ここで、key はキーにしたい型になります (比較可能な型である必要があります https://go.dev/ref/spec#Comparison_operators ) と value は、int から構造体、または別のマップまで、必要に応じて、どのようなタイプであっても、各キーにマップを格納したいタイプになります。

スライスと同様、マップは参照型です。つまり、マップのゼロ値は nil になります。
これは、その下にキーと値を格納するハッシュ テーブルがあり、それらは単なるエンベロープ、抽象化であるために発生します。

次のように宣言すると:

var m map[int]int

その値は nil になります。

値をゼロにしたい場合は、次の宣言を使用できます:

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

地図を並べ替える

冒頭で述べたように、マップでは情報は順序付けされていないため、マップをループするときに情報がどの順序に従うかを指定することはできません。また、Go は実行間の順序が同じであることを保証できません。
配列とスライスで見たように、標準ライブラリには要素の並べ替えに役立つ並べ替えパッケージがあります: https://pkg.go.dev/sort

age を使用した例に従って、sort を使用すると、マップを横断する前にマップのキーをソートできるため、順序どおりにアクセスされることが保証されます。

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

前に見たように、短い宣言で年齢マップを宣言します。
現時点ではキーがないため、キーを格納する文字列スライスを作成し、長さ 0 の make メソッドを使用しますが、マップの長さに対して len メソッドを使用して容量を予約します。
年齢マップを調べてそのキーを保持し、作成されたスライスに追加します。
sort.Strings 関数を使用してキーをアルファベット順に並べ替えます。
すでに注文されているキーのスライスを調べて、問題のキーを使用してマップにアクセスします。
このようにして、マップに秩序正しくアクセスし、プログラムに必要なロジックを実行できます。

同時実行性の問題

マップに関して留意すべき点は、同時に使用するのは安全ではないということです。これらが値にアクセスするか、範囲を指定した for を介して同時読み取りである場合、複数のゴルーチンが同時にアクセスしても問題はありません。
問題が発生するのは、マップに要素を追加または削除することでマップの値を同時に更新すると同時に、別の側からマップを読み取る場合です。
などです。 この状況を解決するには、考えられる解決策がいくつかありますが、詳しくは説明しません。簡単に言及し、さらに深く掘り下げるかどうかはあなたの選択に任せます。

標準ライブラリの同期パッケージ https://pkg.go.dev/sync を使用すると、異なるゴルーチン間の同期を制御できます。
考えられる使用法は、型への読み取りと書き込みをロックおよびロック解除できる RWMutex 型です。したがって、sync.RWMutex とマップを含む型がある場合、いつアクセスできるかを制御できます。
同じ同期パッケージ内で調査するもう 1 つの興味深いタイプは Map です。Map は、マップの操作に役立つ一連の関数をすでに提供していますが、前のソリューションのように、最終的にはネイティブに操作できなくなります。
実装するユースケースに応じて、どちらかが私たちにとってより有用になります。どちらが他より優れているということはありません。それは常に必要なものによって決まります。

この投稿で私が説明しようとしたことがすべて明確であることを願っていますが、完全に明確ではない部分がある場合、または私に説明していない部分がある場合は、ご退席ください。ここにコメントしていただくか、私のプロフィールにあるソーシャル ネットワークを通じてコメントをいただければ、喜んで返信させていただきます。

リリースステートメント この記事は次の場所に転載されています: https://dev.to/charly3pins/maps-in-go-5a3j?1 侵害がある場合は、[email protected] に連絡して削除してください。
最新のチュートリアル もっと>

免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。

Copyright© 2022 湘ICP备2022001581号-3