ユーニックス総合研究所

  • home
  • archives
  • rust-hashmap

Rustのハッシュマップ(HashMap)の使い方【挿入、参照、走査】

  • 作成日: 2024-02-17
  • 更新日: 2024-02-17
  • カテゴリ: Rust

Rustではハッシュマップを使いたい場合はstd::collectionsHashMapを使います。
この記事ではRustのHashMapの使い方を解説していきます。

参考
キーとそれに紐づいた値をハッシュマップに格納する - The Rust Programming Language 日本語版

HashMapにおけるキーと値

ハッシュマップとは文字列のキーに値を紐づけて管理するデータ構造のことです。
たとえばkey1という文字列に123という整数を紐づけます。
こうするとkey1を参照することで123という整数を得ることができます。

ハッシュマップの他にはハッシュテーブルや単にマップ、辞書などと表現されることがあります。
現代的なプログラミングではハッシュマップは非常によく使われるデータ構造です。

HashMapの作成(CREATE)

Rustでハッシュマップを作成するにはまずstd::collections::HashMapuseします。

use std::collections::HashMap;  

そして以下のようなコードでハッシュマップの変数を作成します。

let mut map = HashMap::new();  

HashMap::new()でハッシュマップを新規作成できます。

キーと値の型の指定

HashMapのキーと値の型は自動で判別されます。
そのためinsert()などを実行すればその時に使ったキーと値がハッシュマップの型になります。
明示的に型を指定したい場合は以下のようにします。

let mut int_map = HashMap::<String, i32>::new();  
let mut float_map = HashMap::<String, f32>::new();  

上記のint_mapfloat_mapはともにStringをキーとするハッシュマップです。
値はint_mapi32float_mapf32になります。

キーに値を挿入する(INSERT)

HashMapのキーに対して値を紐づけることを「値を挿入する」と言います。
以下のように値を挿入できます。

    // キーに値を挿入する  
    let mut map = HashMap::new();  
    let key = String::from("key1");  
    let val = String::from("val1");  

    map.insert(key, val);  // keyにvalを挿入  

keyはキーの文字列、valは値の文字列です。
そして挿入するにはinsert()を使います。
insert()の第1引数にキーを指定し第2引数に値を指定します。

Stringのようなオブジェクトはinsert()するとムーブされますので、以降の変数へのアクセスは出来なくなります。

    let key = String::from("key1");  
    let val = String::from("val1");  

    map.insert(key, val);  // keyにvalを挿入  

    println!("{key} {val}");    
    // Error! key and val was moved  

いろいろな型の挿入

HashMapに指定できる型は色々ありますので、挿入方法もそれだけバリエーションがあります。
以下は一例です。

    // キーに値を挿入する  
    let mut int_map = HashMap::new();  

    int_map.insert(String::from("key1"), 10);  
    int_map.insert(String::from("key2"), 20);  

    let mut int_map = HashMap::<&str, i32>::new();  

    int_map.insert("key1", 10);  
    int_map.insert("key2", 20);  

    let mut float_map = HashMap::<&str, f32>::new();  

    float_map.insert("key1", 3.14);  
    float_map.insert("key2", 1.23);  

一度確定した型以外の値を挿入しようとするとエラーになります。

    let mut map = HashMap::new();  
    map.insert("oioi", 123);  // intを入れているのに  
    map.insert("oioi", 3.14);  // floatを入れようとしている  
    // expected integer, found floating-point number  

キーの値の取得(READ)

特定のキーの値を取得したい場合はget()を使います。

    // キーの値の取得  
    let mut map = HashMap::new();  
    let key = String::from("key1");  
    let val = String::from("val1");  

    map.insert(key, val);  // keyにvalを挿入  
    // key and val moved  

    let key = String::from("key1");  
    let val = map.get(&key).unwrap();  
    println!("{val}");  // val1  

Stringの変数(key)でget()を参照したい場合は変数のアドレスを渡す必要があります。

    // &keyでアドレスを渡す  
    let val = map.get(&key).unwrap();  // 値の取得  

&strの場合は明示的に&を付ける必要はありません。

    let mut map = HashMap::new();  
    map.insert("key1", "val1");  
    let val = map.get("key1").unwrap();  // 値の取得  

get()Optionを返しますのでunwrap()が必要です。

キーが存在しない場合のget()

get()はキーが存在しない場合はNoneを返します。

    // キーの値の取得  
    let mut map = HashMap::new();  
    map.insert("key1", "val1");  

    map.get("not found").unwrap();  
    // thread 'main' panicked at 'called `Option::unwrap()` on a `None` value',  

上記では存在しないキーnot foundを参照して返ってきたNoneunwrap()しているのでパニックになっています。
get()の返り値がNoneかどうかチェックしたい場合はifでチェックする方法もあります。

    let mut map = HashMap::new();  
    map.insert("key1", "val1");  

    let opt = map.get("not found");  
    if opt == None {  
        println!("None");  
    }  

またちゃんとエラーハンドリングしたい場合は以下のようにmatchを併用します。

    match map.get("not found") {  
        Some(val) => {  
            println!("{val}");  
        }  
        None => {  
            println!("None");  
        }  
    }  

上記の場合はmapにはnot foundというキーは含まれていません。
ですのでget()の返り値はNoneになります。
よってprintln!("None");が実行されます。

forで回す

HashMapをforで回したい場合は以下のようにします。

    // forで回す  
    let mut map = HashMap::new();  
    map.insert("key1", String::from("val1"));  
    map.insert("key2", String::from("val2"));  
    map.insert("key3", String::from("val3"));  

    for (key, val) in map {  
        println!("{key} = {val}");  
    }  

keyにキー、valに値が格納されます。
明示的にiter()を呼び出すことも可能です。

    for (key, val) in map.iter() {  
        println!("{key} = {val}");  
    }  

forで回して値を更新する(UPDATE)

iter_mut()を使えばHashMapの値をforで回しながら更新できます。

    // forで回して値を更新する  
    let mut map = HashMap::new();  
    map.insert("key1", String::from("val1"));  
    map.insert("key2", String::from("val2"));  
    map.insert("key3", String::from("val3"));  

    for (_, val) in map.iter_mut() {  
        val.push_str(" here!");  
    }  

    println!("{}", map.get("key1").unwrap());  
    // val1 here!  
    println!("{}", map.get("key2").unwrap());  
    // val2 here!  
    println!("{}", map.get("key3").unwrap());  
    // val3 here!  

上記ではmap.iter_mut()でforを回してvalpush_str()で文字列を追記しています。
mapの各キーの値の文字列に派here!という文字列が追記されますので、結果は上記のようになります。
このようにHashMapに格納した値はいつでも変更できます。

同一のキーの値を上書きする(UPDATE)

HashMapでは同一のキーにインサートした場合は値は上書きされます。

    // 同一キーの値の上書き  
    let mut map = HashMap::new();  

    map.insert("oioi", String::from("waiwai"));  

    let val = map.get("oioi").unwrap();  
    println!("{val}");  // waiwai  

    map.insert("oioi", String::from("heyhey"));  

    let val = map.get("oioi").unwrap();  
    println!("{val}");  // heyhey  

上記ではまずmapoioiというキーでwaiwaiという値の文字列を挿入しています。
その後、oioiheyheyという値を挿入して値を上書きしています。

キーが存在しない場合に挿入する(INSERT)

キーが存在しない場合のみに値を挿入したいケースもあるかと思います。
そういう時はentry()を組み合わせて使えます。

    // キーが存在しない場合のみ値を挿入する  
    let mut map = HashMap::new();  

    map.entry("oioi").or_insert(10);  
    map.entry("oioi").or_insert(20);  
    map.entry("waiwai").or_insert(30);  

    println!("{}", map.get("oioi").unwrap());  // 10  
    println!("{}", map.get("waiwai").unwrap());  // 30  

上記ではまずentry()でキーが存在するか調べています。
その後、or_insert()でキーが存在しない場合に値を挿入しています。

キーを削除する(DELETE)

キーと値を削除したい場合はremove()が使えます。

    // キーを削除する  
    let mut map = HashMap::new();  

    map.insert("key1", 1);  
    map.insert("key2", 2);  
    println!("{}", map.len());  // 2  

    // key1を削除  
    map.remove("key1");  

    // key2を削除  
    match map.remove("key2") {  
        Some(val) => {  
            println!("{} removed", val);  // 2 removed  
        }  
        None => {  
            println!("key not found");  
        }  
    }  

    println!("{}", map.len());  // 0  

remove()Optionを返します。
remove()の第1引数に削除したいキーを指定します。
削除に成功すると削除した値を、キーが存在しない場合はNoneを返します。