RustのHashMap
HashMap は、データをキーと値のペアとして保存します。配列やベクタのように位置で探すのではなく、キーを使って値を探します。Pythonの辞書(dict)や、JavaScriptでオブジェクトをマップとして使ったことがあれば、それと同じ考え方です。
HashMapを作る
Section titled “HashMapを作る”use std::collections::HashMap;
fn main() { let mut ages = HashMap::new();
ages.insert("Alice", 30); ages.insert("Bob", 25);
println!("{:?}", ages);}先頭の use std::collections::HashMap; で、この型をスコープに持ち込んでいます。Vec や String と違って HashMap は最初から使えるわけではないので、この行が必要です。
use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30);
match ages.get("Alice") { Some(age) => println!("Aliceは{age}歳です"), None => println!("見つかりません"), }}.get() は値をそのまま返すのではなく、Option を返します。キーが存在しないかもしれないからです。Option については専用のページできちんと説明します。今のところは、match で Some と None を処理するのが基本の書き方だと覚えておいてください。
値を更新する
Section titled “値を更新する”同じキーでもう一度 insert すると、古い値が上書きされます。
use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30); ages.insert("Alice", 31); // 30を上書きする
println!("{:?}", ages.get("Alice"));}キーがまだ存在しないときだけ値を追加したいなら、.entry() を使います。
use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30);
ages.entry("Alice").or_insert(99); // Aliceはすでにあるので、何もしない ages.entry("Bob").or_insert(25); // Bobはまだないので、追加される
println!("{:?}", ages);}HashMapをループで回す
Section titled “HashMapをループで回す”use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30); ages.insert("Bob", 25);
for (name, age) in &ages { println!("{name}は{age}歳です"); }}HashMapを使うのはどんなとき?
Section titled “HashMapを使うのはどんなとき?”位置ではなく、名前やIDで何かを探したいときは HashMap の出番です。たとえば、単語の出現回数を数える、ユーザー名とユーザー情報を対応づける、計算結果をキーごとにキャッシュする、といった場面です。順番が大事なときや、ただのリストを順に処理するだけなら、Vec のほうが向いています。
1. 小さな電話帳
Section titled “1. 小さな電話帳”3人の名前と電話番号を HashMap に保存します。次に、電話帳に載っている "Ben" と、載っていない "Dana" を探して、それぞれについてわかりやすい1行を表示してください。ヒント:["Ben", "Dana"] をループして、.get(name) に対して match します。
解答を見る
use std::collections::HashMap;
fn main() { let mut phone_book = HashMap::new(); phone_book.insert("Ana", "555-1234"); phone_book.insert("Ben", "555-9876"); phone_book.insert("Chen", "555-4321");
for name in ["Ben", "Dana"] { match phone_book.get(name) { Some(number) => println!("{name}: {number}"), None => println!("{name}: 電話帳に載っていません"), } }}実行結果:
Ben: 555-9876Dana: 電話帳に載っていませんマップではなく、探したい名前のほうをループしているので、表示の順番も決まります。HashMap から予測どおりの出力を得たいときに便利なテクニックです。
2. 票を数える
Section titled “2. 票を数える”["赤", "青", "赤", "緑", "青", "赤"] という票があります。それぞれの色が何票入ったかを数えて、"赤"、"緑"、"青"、そして1票も入らなかった "ピンク" の合計を表示してください。ヒント:counts.entry(color).or_insert(0) で、その色の今の票数が得られます(最初は0)。*count += 1 で1を足します。
解答を見る
use std::collections::HashMap;
fn main() { let votes = ["赤", "青", "赤", "緑", "青", "赤"]; let mut counts = HashMap::new();
for color in votes { let count = counts.entry(color).or_insert(0); *count += 1; }
for color in ["赤", "緑", "青", "ピンク"] { match counts.get(color) { Some(n) => println!("{color}: {n}"), None => println!("{color}: 0"), } }}実行結果:
赤: 3緑: 1青: 2ピンク: 0or_insert(0) が返すのは、マップに保存されている値のコピーではなく、その値への可変参照です。*count += 1 の * は「これが指している数を変更する」という意味なので、マップそのものが更新されます。この「entryで探し、なければ0を入れて、1を足す」という書き方は、Rustで何かを数えるときの定番です。
HashMap<K, V>はキーと値のペアを保存します。use std::collections::HashMap;でスコープに持ち込みます。.insert(key, value)は追加または上書きをします。.get(key)は値を探してOptionを返します。.entry(key).or_insert(value)は、キーがまだないときだけ値を追加します。- ループの順番は保証されません。順番に頼らないようにしましょう。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. Vec や String と違って、HashMap をスコープに持ち込むには何が必要ですか?
2. .entry(key).or_insert(value) は何をしますか?
3. HashMap をループで回したときの表示順に頼ってよいですか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- キーとそれに紐づいた値をハッシュマップに格納する — The Rust Programming Language 日本語版:キーと値の所有権や、値を更新しながら単語を数える例を扱っています。
- ハッシュマップ — Rust By Example 日本語版:
insert、get、removeとループを使った、連絡先の実行できるサンプルです。 HashMap— 標準ライブラリのドキュメント(英語):contains_key、remove、keys、valuesなど、すべてのメソッドが載っています。Entry— 標準ライブラリのドキュメント(英語):or_insert_withやand_modifyなど、.entry()でできることがすべてわかります。