RustのHashMap入門:基本操作から頻出パターンまとめ

スポンサーリンク

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() を使うと「なければ追加・あれば更新」が簡潔に書ける
  • カウントや集計処理に entry API が特に便利
  • 順序は保証されない(挿入順が必要なら IndexMap クレート)

RustのVec初期化については「RustのVec初期化まとめ:vec!マクロからVec::newまで」を参照してください。

RustのStringと&strの違いは「RustのStringと&strの違い:使い分けと変換方法まとめ」を参照してください。

Rustのイテレータ(map・filter・collect)は「Rustのイテレータ入門:map・filter・collectの使い方」を参照してください。