概要

HashMap (ハッシュマップ) とは、複数のデータ型を管理することができるコレクション型である。
連想配列とも呼ばれる。

HashMapの特徴は、要素を格納する時に、キーを使用して管理することができる。

RustのHashMapは標準ライブラリに含まれているが、使用する場合には std::collections::HashMap をインポートする必要がある。

HashMapは要素の順番を保持しない。
順番を保持したい場合は、BTreeMapIndexMap 等の別のデータ構造を使用する。


HashMapの定義

HashMapを使用するには、std::collections::HashMap をインポートする必要がある。
HashMapの作成には、new メソッドを使用、または、マクロを使用する方法がある。

基本的な宣言方法は、HashMap::new()で空のHashMapを生成して、insert メソッドで要素を追加する。

 use std::collections::HashMap;
 
 // 空のHashMapを作成
 let mut map = HashMap::new();
 
 // 要素を追加
 map.insert("x", 100);
 map.insert("y", 200);
 map.insert("z", 300);


また、最初から値を持つHashMapを作成することもできる。

 use std::collections::HashMap;
 
 let mut map = HashMap::from([
    ("x", 100),
    ("y", 200),
    ("z", 300),
 ]);



HashMapの操作

HashMapはキーでそれぞれの値を抽出することができる。

get メソッドを使用してキーを指定することで、値を取得できる。
get メソッドはOption型を返すため、値が存在しない場合に安全に処理できる。

 use std::collections::HashMap;
 
 let mut map = HashMap::new();
 map.insert("x", 100);
 map.insert("y", 200);
 
 // get()はOption<&V>を返すため、unwrap や .copied()を使用する
 println!("{}", map.get("x").unwrap());
 println!("{}", map.get("y").unwrap());
 
 // 出力
 100
 200


次は、キーを指定して値を変更する。
insert メソッドで既存のキーに値を設定すると、値が上書きされる。

 use std::collections::HashMap;
 
 let mut map = HashMap::new();
 map.insert("x", 100);
 map.insert("y", 200);
 
 // 既存のキーに新しい値を設定
 map.insert("x", 300);
 println!("{:?}", map);
 
 // 出力
 {"x": 300, "y": 200}


次に、新たにデータをHashMapに追加する。
以下の例では、新しいキーを指定して、新しい要素を追加している。(文字列のキーと数値のキーの両方で行う)

 use std::collections::HashMap;
 
 let mut map1 = HashMap::new();
 map1.insert("x", 100);
 map1.insert("y", 200);
 map1.insert("z", 300);
 
 // 数値のキーを持つHashMap
 let mut map2 = HashMap::new();
 map2.insert(10, 400);
 
 println!("{:?}", map1);
 println!("{:?}", map2);
 
 // 出力
 {"x": 100, "y": 200, "z": 300}
 {10: 400}



イテレータからHashMapを作成する

HashMapは、イテレータを持つコレクション (配列、ベクター、タプル等) を collect メソッドを使用して変換して作成することができる。
それぞれキーと要素との2つの組み合わせになったものであることが必要である。

以下の例では、タプルの配列やベクタからcollectメソッドでHashMapに変換する。

 use std::collections::HashMap;
 
 // タプルの配列から作成
 let pairs = [("a", "b"), ("c", "d"), ("e", "f")];
 let map1: HashMap<_, _> = pairs.into_iter().collect();
 
 // ベクターから作成
 let vec_pairs = vec![("a", "b"), ("c", "d"), ("e", "f")];
 let map2: HashMap<_, _> = vec_pairs.into_iter().collect();
 
 println!("{:?}", map1);
 println!("{:?}", map2);
 
 // 出力
 {"a": "b", "c": "d", "e": "f"}
 {"a": "b", "c": "d", "e": "f"}


また、2つの別々のベクタから zip メソッドを使用してHashMapを作成することもできる。

 use std::collections::HashMap;
 
 let keys = vec!["x", "y"];
 let values = vec![100, 200];
 let map: HashMap<_, _> = keys.into_iter().zip(values.into_iter()).collect();
 
 println!("{:?}", map);
 
 // 出力
 {"x": 100, "y": 200}



HashMapのメソッド

HashMap型には、様々なメソッドがある。

keysメソッド と valuesメソッド で全てのキーと値を取得する

これらのメソッドは、それぞれキーの全て、値の全てを取得する時に使用する。
イテレータを返すため、ループ処理や他のコレクションへの変換に使用できる。

 use std::collections::HashMap;
 
 let mut map = HashMap::new();
 map.insert("x", 100);
 map.insert("y", 200);
 
 // キーを取得
 for key in map.keys() {
    println!("キー: {}", key);
 }
 
 // 値を取得
 for value in map.values() {
    println!("値: {}", value);
 }
 
 // 出力
 キー : x
 キー : y
    : 100
    : 200


extendメソッドでHashMapを結合する

extendメソッドは、2つの異なるHashMapを結合することができる。
キーが同じ場合は新しい値に更新する。

 use std::collections::HashMap;
 
 let mut map1 = HashMap::from([
    ("l", 10),
    ("m", 200),
    ("n", 300),
 ]);
 
 let map2 = HashMap::from([
    ("l", 100),
    ("o", 400),
 ]);
 
 map1.extend(map2);
 println!("{:?}", map1);
 
 // 出力
 {"l": 100, "m": 200, "n": 300, "o": 400}


getメソッドで値を取得する

getメソッドを使用してキーを指定し、値を取得することができる。
getメソッドはOption<&V>を返すため、キーが存在しない場合にもパニックせず、Noneが返される。

 use std::collections::HashMap;
 
 let mut map = HashMap::new();
 map.insert("x", 10);
 map.insert("y", 20);
 
 // 値を取得
 let val = map.get("x");
 match val {
    Some(v) => println!("{}", v),
    None => println!("キーが存在しません"),
 }
 
 // 出力
 10


removeメソッドで値を取得して削除する

removeメソッドはキーを指定して値を取得し、HashMapからその要素を削除する。
Option<V>を返すため、キーが存在しない場合はNoneが返される。

 use std::collections::HashMap;
 
 let mut map = HashMap::from([
    ("x", 100),
    ("y", 200),
    ("z", 300),
 ]);
 
 let value = map.remove("x");
 println!("{:?}", value);
 println!("{:?}", map);
 
 // 出力
 Some(100)
 {"y": 200, "z": 300}


個別の要素を削除する

remove メソッドを使用すると、指定したキーの要素をHashMapから削除することができる。

 use std::collections::HashMap;
 
 let mut map = HashMap::from([
    ("x", 100),
    ("y", 200),
    ("z", 300),
 ]);
 
 map.remove("y");
 println!("{:?}", map);
 
 // 出力
 {"x": 100, "z": 300}


HashMapの変数自体が不要になった場合は、Rustの所有権システムによりスコープを抜けた時点で自動的にメモリが解放される。

 {
    let map = HashMap::new();
    // mapを使用
 } // ここでmapは自動的に解放される


clearメソッドで全ての要素を削除する

clearメソッドを使用すると、全ての要素を削除して空のHashMapが残り、HashMap自体の操作を続けることができる。

 use std::collections::HashMap;
 
 let mut map = HashMap::from([
    ("x", 100),
    ("y", 200),
    ("z", 300),
 ]);
 
 map.clear();
 println!("{:?}", map);
 
 // 出力
 {}


contains_keyメソッドでキーの有無を調べる

HashMapに指定するキーがあるかを調べるには、contains_key メソッドを使用する。
HashMapにキーが存在すればtrue、キーが存在しない場合はfalseを返す。

 use std::collections::HashMap;
 
 let mut map = HashMap::from([
    ("x", 100),
    ("y", 200),
    ("z", 300),
 ]);
 
 println!("{}", map.contains_key("x"));
 println!("{}", map.contains_key("a"));
 
 // 出力
 true
 false



HashMapのクローン

HashMapをコピーするには、代入による所有権の移動と clone メソッドを使用する方法がある。
Rustの所有権システムにより、単純な代入では所有権が移動し、元の変数は使用できなくなる。

代入による所有権の移動

単純な代入では、所有権が移動するため、元の変数は使用できなくなる。

 use std::collections::HashMap;
 
 let mut map1 = HashMap::from([
    ("x", 100),
    ("y", 200),
    ("z", 300),
 ]);
 
 let mut map2 = map1; // 所有権が移動
 map2.insert("x", 500);
 
 println!("{:?}", map2);
 // println!("{:?}", map1); // エラー: map1は既に使用できない
 
 // 出力
 {"x": 500, "y": 200, "z": 300}


cloneメソッドで独立したコピーを作成する

全く独立した別のHashMapとして扱えるようにするには、clone メソッドを使用する。
cloneメソッドはディープコピーを行うため、一方を変更しても他方には影響しない。

 use std::collections::HashMap;
 
 let mut map1 = HashMap::from([
    ("x", 100),
    ("y", 200),
    ("z", 300),
 ]);
 
 let mut map2 = map1.clone(); // クローンを作成
 map2.insert("x", 500);
 
 println!("{:?}", map1);
 println!("{:?}", map2);
 
 // 出力
 {"x": 100, "y": 200, "z": 300}
 {"x": 500, "y": 200, "z": 300}