Go言語のマップのキーに使える型と制限を解説!初心者でもわかるマップの基本
生徒
「先生、Go言語のマップって何でもキーに使えるんですか?どんな型が使えるか教えてほしいです。」
先生
「マップのキーに使える型には決まりがあります。少しわかりにくいので、わかりやすく説明しますね。」
生徒
「はい、お願いします!」
先生
「では、まずマップのキーとは何かから説明して、その後で使える型や制限を詳しく見ていきましょう!」
1. Go言語のマップとは?キーと値の組み合わせ
Go言語のマップは、「キー」と「値」をセットで管理するための入れ物です。よく「辞書」や「電話帳」にたとえられます。たとえば、名前(キー)を渡すと、その人の電話番号(値)が返ってくるようなイメージです。「この名前の人の情報をすぐ取り出したい」といった場面でとても役に立ちます。
もう少し噛み砕くと、キーは「探すときに使うラベル」、値は「そのラベルに対応する中身」です。同じキーは1つしか持てないので、「名前 → 電話番号」「商品コード → 在庫数」のように、1つのキーに1つの情報を結びつけるデータ構造だと考えるとイメージしやすくなります。
簡単なサンプルで、Go言語のマップがどんな動きをするのか見てみましょう。
package main
import "fmt"
func main() {
// 名前(キー)と電話番号(値)を管理するマップを作成
phoneBook := map[string]string{
"山田": "090-1234-5678",
"佐藤": "080-9876-5432",
}
// キーを使って値を取り出す
fmt.Println("山田さんの電話番号:", phoneBook["山田"])
// 存在しないキーを指定すると、空の値(この場合は空文字列)が返る
fmt.Println("田中さんの電話番号:", phoneBook["田中"])
}
このコードでは、phoneBook というマップに「山田」「佐藤」というキーを登録し、それぞれの電話番号を値として持たせています。phoneBook["山田"]のようにキーを指定すると、対応する電話番号をすぐに取り出すことができます。一方で、まだ登録していない「田中」というキーを指定すると、何も入っていない状態(文字列なら空文字列)が返ってきます。
このように、Go言語のマップは「キーで値を素早く探すための箱」として使えるため、設定情報やユーザー情報、集計結果など、さまざまな場面で活躍します。まずは「キーと値のペアをまとめて管理する仕組み」だと押さえておくと、次の「どんな型をキーにできるのか」という話も理解しやすくなります。
2. マップのキーに使える型とは?
マップのキーに使える型は、Go言語では「比較できる型(比較可能な型)」に限られています。ここでいう比較とは、==(等しい)や!=(等しくない)を使って、2つの値が同じかどうかを判定できることを指します。「このキーとあのキーが同じか?」をはっきり判定できる型だけが、マップのキーとして認められているイメージです。
なぜこれが大事かというと、マップは中で「キーを見比べる」ことで値を探しているからです。たとえば、"山田"というキーで検索するとき、マップは登録されているキーと順番に比べて「同じかどうか」を判断します。この比較ができなければ、「一致したかどうか」がわからず、正しく値を取り出せません。
もう少し身近なイメージでいうと、「出席番号 → 生徒の名前」の一覧を考えるとわかりやすいです。出席番号(1, 2, 3...)は数字として==で比べられるので、そのままマップのキーにできます。一方で、「形がバラバラで比べ方が決めづらいもの」はキーに向きません。
簡単なサンプルコードで、どのようにキーが使われるか確認してみましょう。
package main
import "fmt"
func main() {
// 整数(int)をキー、文字列を値にしたマップ
studentIDs := map[int]string{
1: "山田",
2: "佐藤",
}
// 文字列(string)をキーにしたマップ
scores := map[string]int{
"国語": 80,
"数学": 90,
}
fmt.Println("1番の生徒:", studentIDs[1])
fmt.Println("数学の点数:", scores["数学"])
}
この例では、studentIDs では int がキー、scores では string がキーとして使われています。どちらも == で比較できる型なので、問題なくマップのキーとして利用できます。逆に、スライスのように簡単に比較できない型はキーには使えません(どの型が使えてどの型が使えないかは、次の項目で具体的に見ていきます)。
まずは「Go言語のマップのキーには、必ず比較可能な型が必要になる」という考え方だけ押さえておけば、このあとの詳しい説明も理解しやすくなります。
3. 具体的に使える型の例
では、実際にどんな型がマップのキーとして使えるのでしょうか。基本的には「比較できる型」であれば利用できます。ここでは初心者でもイメージしやすいように、それぞれの型がどんな場面で使われるのかを交えながら説明します。
- 数値型(int, float64 など)
出席番号や商品IDのように「番号で管理したい」場面でよく使われます。数値は==で簡単に比較できるため、マップのキーに最適です。 - 文字列型(string)
名前やコード名、科目名など、人が識別しやすいラベルをキーにしたい場合に向いています。もっとも使用頻度の高いキー型です。 - ポインタ型(*int など)
ポインタは「メモリ上の場所」を指すため、比較もメモリアドレス同士で行われます。特定のオブジェクトを識別したい場面で使われます。 - チャネル型(chan)
Go言語ならではの型で、並行処理を扱う際に利用します。チャネル同士の比較が可能なため、キーとしても使用できます。 - インターフェース型(条件による)
インターフェースの中身が比較可能な型の場合のみ、その値をキーにできます。型によっては使えない点に注意が必要です。 - 配列型([3]int など)
配列は長さが固定で、すべての要素が比較可能ならキーとして使えます。「3つの属性でひとまとまりの識別子を作りたい」などの用途で使われることもあります。
これらに共通するのは、いずれも「値同士を正しく比べられる」という点です。キーが比較可能であることで、マップはデータを正確に検索できるようになります。
簡単な例を見てみましょう。文字列や数値をキーにしたマップは、実際のアプリケーションでもよく使われます。
package main
import "fmt"
func main() {
// 文字列をキーにした例
colors := map[string]string{
"red": "赤",
"blue": "青",
"green": "緑",
}
// 数値をキーにした例
levels := map[int]string{
1: "初心者",
2: "中級者",
3: "上級者",
}
fmt.Println(colors["blue"]) // 「青」が表示される
fmt.Println(levels[3]) // 「上級者」が表示される
}
このように、マップは比較可能な型をキーにすることで、必要な情報を素早く取り出せる便利な仕組みになります。どの型が使えるか知っておくことで、より柔軟にデータを扱えるようになります。
4. 使えない型の例と理由
逆に、キーとして使えない型もあります。主に次のようなものです。
- スライス型([]int など):サイズが変動し、中身を比較できないため。
- マップ型(map[string]int など):構造が複雑で比較できないため。
- 関数型:関数は比較できないため。
- 構造体(struct):全てのフィールドが比較可能でなければ使えません。
これらは内部で比較できないため、キーには使えません。
5. なぜスライスやマップはキーに使えないの?
スライスやマップは中身が可変で、たとえば2つのスライスが「同じ要素を持っているか?」を自動的に比較できません。だからマップのキーとしては使えないのです。
キーには「このキーとあのキーは同じか違うか」がはっきりわかる型でないといけません。
6. キーに構造体を使う場合の注意点
構造体をキーにしたい場合は、その構造体の全てのフィールドが比較可能な型である必要があります。
もし構造体の中にスライスやマップなど比較できない型が含まれていると、その構造体はキーにできません。
7. 実際のコード例
次のコードは、文字列をキーにしたマップの例です。
phoneBook := map[string]string{
"山田": "090-1234-5678",
"佐藤": "080-9876-5432",
}
fmt.Println(phoneBook["山田"])
一方、スライスをキーに使おうとするとエラーになります。
// エラー例:スライスをキーにするとコンパイルできません
// var invalidMap map[[]int]string
8. ポイント整理
Go言語のマップのキーは「比較可能な型」である必要があり、主に数値、文字列、ポインタ、配列(要素が比較可能な場合)が使えます。
スライス、マップ、関数などはキーに使えません。構造体を使う場合は、全てのフィールドが比較可能か確認しましょう。
これを知っておくと、マップの使い方やエラーの原因を理解しやすくなります。
まとめ
Go言語でマップを扱うときにもっとも大切になるのが、「キーとして使える型には条件がある」という点です。初めて学ぶときには、マップは自由に値を保存できる便利な仕組みという印象が先にくるため、どんな型でもキーにできると考えてしまいがちですが、実際には比較可能な型だけがキーとして使えます。なぜ比較可能であることが重要なのかというと、マップの内部ではキーをもとに一致する値を探すため、どのキーが同じでどのキーが異なるのかを正確に判定する必要があるからです。比較できない型をキーにすると、その判定そのものができなくなるため、コンパイルが通らずエラーになってしまうのです。
数値や文字列、ポインタ、配列など比較可能な型はマップのキーとして標準的に利用することができます。とくに文字列をキーにしたマップは、辞書のように情報を調べる用途に向いており、実務や学習でも最もよく使われます。名前と電話番号、ユーザーIDとプロフィール情報など、さまざまな場面で利用できるため、マップの基本を理解しておくとデータ管理の考え方が一段と広がります。一方で、スライスやマップ、関数など比較不可能な型はマップのキーとしては使えません。スライスはサイズが変わる構造であり、同じ内容のスライス同士を自動的に比較する手段がないため、Go言語では比較不可として扱われています。関数も内部状態を比較する方法が存在しないため同じ扱いとなります。このように、マップに何をキーとして使えるかを理解しておくと、プログラムを書く際に避けるべきエラーがあらかじめわかり、より安定したコードを書くことができるようになります。
また、構造体をマップのキーとして使う場面では、構造体の内部にどんな型を使っているかを意識する必要があります。構造体そのものは比較可能ですが、フィールドの中にスライスやマップなど比較できない型が含まれている場合、その構造体全体が比較不可能になります。そのため、構造体をキーとして利用したいときには、すべてのフィールドが比較可能な型であることを確認することが欠かせません。この点を理解しておけば、複雑なデータ構造を扱う際にも安全にマップを活用でき、扱える情報の幅も大きく広がります。キーとして使える型と使えない型の違いを丁寧に押さえておけば、マップ操作中のバグや意図しない挙動も避けられ、プログラム全体の品質が向上します。
ここまでの内容を踏まえると、マップを使う際には「キーが比較可能かどうか」を常に意識することが非常に重要であるとわかります。比較可能であればスムーズに値を検索でき、比較不可の場合にはコンパイル時にエラーが出て問題点を発見できます。Go言語はこの点を明確に定義しているため、型の性質を理解しながらコードを書く習慣が身につきやすくなります。実際にコードをいくつか作ってみると、マップがどれだけ強力で扱いやすいデータ構造なのかがより実感できるはずです。
マップの理解が深まるサンプルコード
以下は、比較可能な型をキーにしたマップと、構造体をキーにした例です。記事の流れに合わせて、同じクラス名やタグでまとめています。
// 比較可能な型(文字列)を使ったマップ
book := map[string]int{
"数学": 90,
"英語": 85,
"国語": 88,
}
fmt.Println(book["数学"])
// 構造体をキーにする例(全フィールド比較可能)
type Point struct {
X int
Y int
}
location := map[Point]string{
{X: 1, Y: 2}: "入口付近",
{X: 3, Y: 5}: "中央エリア",
}
fmt.Println(location[Point{X: 1, Y: 2}])
文字列や数値だけでなく、構造体も条件を満たせばしっかりとキーとして利用できることがわかります。条件を丁寧にチェックしておくことで、マップの柔軟性と便利さを最大限に引き出すことができます。
生徒
「マップのキーに何でも使えるわけではないということがよくわかりました。比較できるかどうかが大事なんですね。」
先生
「そうです。比較できる型であれば安心してキーに使えますし、比較できない型はそもそもマップでは使えないという特徴を覚えておくと、エラーの予防になりますよ。」
生徒
「スライスやマップが使えない理由も納得できました。構造体をキーにする場合は、フィールドも含めて確認する必要があるんですね。」
先生
「その通りです。型の性質を理解しながら使うことで、マップをもっと柔軟に扱えるようになりますよ。これからいろいろなコードを書いて、自然に使いこなせるようになりましょう。」