RustのHashMap入門:基本操作から頻出パターンまとめ
はじめに
HashMap はキーと値のペアでデータを管理するコレクションです。キーで素早く値を検索・更新できます。Rustでは std::collections::HashMap として提供されています。
インポート
use std::collections::HashMap;
生成
// 空のHashMapを作る let mut map: HashMap<String, i32> = HashMap::new(); // 型推論が効く場合は型注釈を省略できる let mut scores = HashMap::new(); scores.insert(String::from("田中"), 100); // ここで型が確定する
基本操作
追加・更新(insert)
let mut map = HashMap::new(); map.insert("apple", 3); map.insert("banana", 5); // 同じキーで insert すると上書きされる map.insert("apple", 10); // apple の値が 10 になる
取得(get)
let mut map = HashMap::new(); map.insert("apple", 3); // get は Option<&V> を返す match map.get("apple") { Some(count) => println!("apple: {}", count), None => println!("見つかりません"), } // if let でシンプルに書く if let Some(count) = map.get("apple") { println!("apple: {}", count); } // 存在しないキーは None let result = map.get("grape"); // None
削除(remove)
let mut map = HashMap::new(); map.insert("apple", 3); map.remove("apple"); // 削除(戻り値は Option<V>)
キーの存在確認(contains_key)
let mut map = HashMap::new(); map.insert("apple", 3); map.contains_key("apple") // true map.contains_key("grape") // false
entry API(最頻出パターン)
なければ挿入、あれば何もしない
let mut map = HashMap::new(); // "apple" がなければ 0 を挿入する map.entry("apple").or_insert(0); println!("{:?}", map); // {"apple": 0} // すでにある場合は変更されない map.insert("apple", 5); map.entry("apple").or_insert(0); println!("{:?}", map); // {"apple": 5}
カウントアップ(単語の出現回数を数える)
let text = "apple banana apple orange banana apple"; let mut counts = HashMap::new(); for word in text.split_whitespace() { let count = counts.entry(word).or_insert(0); *count += 1; } println!("{:?}", counts); // {"apple": 3, "banana": 2, "orange": 1}
or_insert は &mut V を返すので、*count で値を変更できます。
なければ計算して挿入(or_insert_with)
let mut map: HashMap<&str, Vec<i32>> = HashMap::new(); map.entry("scores").or_insert_with(Vec::new).push(100); map.entry("scores").or_insert_with(Vec::new).push(200); println!("{:?}", map); // {"scores": [100, 200]}
イテレーション
let mut map = HashMap::new(); map.insert("apple", 3); map.insert("banana", 5); map.insert("orange", 2); // キーと値を両方取得 for (key, value) in &map { println!("{}: {}", key, value); } // キーだけ for key in map.keys() { println!("{}", key); } // 値だけ for value in map.values() { println!("{}", value); } // 値を変更しながらイテレート for value in map.values_mut() { *value *= 2; }
※ HashMap の順序は保証されていません。挿入順を保持したい場合は IndexMap(外部クレート)を使います。
Vec から HashMap を作る(collect)
let teams = vec!["田中", "鈴木", "佐藤"]; let scores = vec![100, 80, 90]; let map: HashMap<_, _> = teams.iter().zip(scores.iter()).collect(); println!("{:?}", map); // {"田中": 100, "鈴木": 80, "佐藤": 90}
所有権の注意点
String など所有権のある型を insert すると、HashMap がその値の所有権を持ちます。
let key = String::from("apple"); let value = String::from("りんご"); let mut map = HashMap::new(); map.insert(key, value); // key と value はここで使えなくなる(所有権が移動したため) // println!("{}", key); // コンパイルエラー
参照を挿入する場合はライフタイムが絡みます。手軽に使うなら clone() で複製するか、そもそも &str など Copy トレイトを実装した型をキーにするのが簡単です。
よく使うメソッド一覧
| メソッド | 説明 |
|---|---|
insert(k, v) |
キーと値を追加(上書き) |
get(k) |
値を取得(Option<&V>) |
get_mut(k) |
値を可変参照で取得(Option<&mut V>) |
remove(k) |
キーと値を削除(Option<V>) |
contains_key(k) |
キーが存在するか |
entry(k).or_insert(v) |
なければ挿入、あればそのまま |
len() |
要素数 |
is_empty() |
空かどうか |
keys() |
キーのイテレータ |
values() |
値のイテレータ |
iter() |
(key, value) のイテレータ |
まとめ
HashMap::new()で生成、insert/get/removeが基本操作entry().or_insert()を使うと「なければ追加・あれば更新」が簡潔に書ける- カウントや集計処理に
entryAPI が特に便利 - 順序は保証されない(挿入順が必要なら
IndexMapクレート)
RustのVec初期化については「RustのVec初期化まとめ:vec!マクロからVec::newまで」を参照してください。
RustのStringと&strの違いは「RustのStringと&strの違い:使い分けと変換方法まとめ」を参照してください。
Rustのイテレータ(map・filter・collect)は「Rustのイテレータ入門:map・filter・collectの使い方」を参照してください。