Rustのハッシュマップ(HashMap)の使い方【挿入、参照、走査】
- 作成日: 2024-02-17
- 更新日: 2024-02-17
- カテゴリ: Rust
Rustではハッシュマップを使いたい場合はstd::collections
のHashMap
を使います。
この記事ではRustのHashMap
の使い方を解説していきます。
参考
キーとそれに紐づいた値をハッシュマップに格納する - The Rust Programming Language 日本語版
HashMapにおけるキーと値
ハッシュマップとは文字列のキーに値を紐づけて管理するデータ構造のことです。
たとえばkey1
という文字列に123
という整数を紐づけます。
こうするとkey1
を参照することで123
という整数を得ることができます。
ハッシュマップの他にはハッシュテーブルや単にマップ、辞書などと表現されることがあります。
現代的なプログラミングではハッシュマップは非常によく使われるデータ構造です。
HashMapの作成(CREATE)
Rustでハッシュマップを作成するにはまずstd::collections::HashMap
をuse
します。
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_map
とfloat_map
はともにString
をキーとするハッシュマップです。
値はint_map
がi32
でfloat_map
がf32
になります。
キーに値を挿入する(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
を参照して返ってきたNone
をunwrap()
しているのでパニックになっています。
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を回してval
にpush_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
上記ではまずmap
にoioi
というキーでwaiwai
という値の文字列を挿入しています。
その後、oioi
にheyhey
という値を挿入して値を上書きしています。
キーが存在しない場合に挿入する(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
を返します。