【Rust】HashMapの基本と便利なメソッドの使い方

今回はHashMap型について見ていきたいと思います。

名前こそ違えど多言語にも存在するデータ構造ですのでわりと理解しやすいです。

今までと同様、自身の勉強のアウトプット・整理と同時に、同じようにRustを勉強している方や「公式ドキュメントはちょっと、、」というような方が気楽に見れるようにサンプルコードをメインに簡単にまとめてみました。

この記事でできること
  • HashMapの基本
  • HashMapの便利なメソッドとその使い方
目次

基本

HashMapキーと値のペア要素コレクション型です。

例えば以下のコードではキーが数値型、値が文字列型のHashMapとなります。

use std::collections::HashMap; // HashMapの使用にはuseが必要なことに注意!

fn sample() {
    let mut map: HashMap<i32, &str> = HashMap::new();
    map.insert(1, "test");
    map.insert(2, "test2");

    println!("{:?}", map);
    // -> {2: "test2", 1: "test"}
}

後述のメソッド紹介でも記載しますが、宣言と初期化を同時に行うにはfromが便利です。

fn sample() {
    let map = HashMap::from([(1, "test"), (2, "test2")]);
    println!("{:?}", map);
    // -> {1: "test", 2: "test2"}
}

HashMapにおいて同一のキーは重複できません。
重複するキーが発生した場合の動作はメソッドにより異なります。

HashMapの操作

要素へのアクセス

HashMapの要素へアクセスするには以下のようにします。

fn sample() {
    let map = HashMap::from([(1, "test"), (2, "test2")]);

    println!("{:?}", map[&1]); // -> "test"
    println!("{:?}", map[&3]); // -> 存在しないキーによるアクセスはpanicとなる

    println!("{:?}", map.get(&1)); // -> Some("test")
    println!("{:?}", map.get(&3)); // -> None
}

インデックスアクセスも使用できますが、getなどのメソッドを使うほうが安全です。

ループ処理

コレクション型ですのでループ処理もサポートされています。

fn sample() {
    let map = HashMap::from([(1, "test"), (2, "test2")]);

    for (key, value) in map.iter() {
        println!("key: {}, value: {}", key, value);
        // -> key: 2, value: test2
        // -> key: 1, value: test
    }
}

注意が必要なこととして、HashMapでは順序が保証されません

キーの昇順/降順で並べたい場合は同様のコレクション型であるBTreeMapが使用できます。

便利なメソッド

以降はHashMapで使用できる便利なメソッドをいくつかピックアップします。

from

タプル配列を元にHashMapを初期化する。

fn sample() {
    let map = HashMap::from([
        (1, "test"),
        (2, "test2"),
        (3, "test3"),
        (4, "test4"),
        (5, "test5"),
    ]);
    println!("{:?}", map);
    // -> {3: "test3", 2: "test2", 5: "test5", 4: "test4", 1: "test"}
}

get

指定したキーに対応する値をResult型で返す。

fn sample() {
    let map = HashMap::from([(1, "test"), (2, "test2")]);
    println!("{:?}", map.get(&1)); // -> Some("test")
    println!("{:?}", map.get(&5)); // -> None
}

iter

iter

イテレータを要素への参照として取得する。

fn sample() {
    let map = HashMap::from([(1, "test"), (2, "test2")]);

    for v in map.iter() {
        println!("{:?}", v);
    }

    // 複数回イテレータを使用可能
    for v in map.iter() {
        println!("{:?}", v);
    }
}
into_iter

イテレータを要素の所有権を移動した状態で取得する

fn sample() {
    let map = HashMap::from([(1, "test"), (2, "test2")]);

    for v in map.into_iter() {
        println!("{:?}", v);
    }

    // 一度into_iterしたあとは所有権が移動しているためmapに対する操作はできない
    for v in map.into_iter() {
        println!("{:?}", v);
    }
}
iter_mut

イテレータを要素への可変参照として取得する。

fn sample() {
    let mut map = HashMap::from([(1, "test"), (2, "test2")]);

    for v in map.iter_mut() {
        *v.1 = "dummy";
        println!("{:?}", v);
    }

    // iterと同様に複数回イテレータを使用可能
    for v in map.iter_mut() {
        println!("{:?}", v);
    }
}

insert

指定したキーの要素が存在しない場合は新しい要素を追加する。

すでに同一キーの要素が存在する場合はその要素の値を更新する。

fn sample() {
    let mut map = HashMap::from([(1, "test"), (2, "test2")]);

    let result = map.insert(3, "test3");
    println!("{:?}", map);    // -> {2: "test2", 3: "test3", 1: "test"}
    println!("{:?}", result); // -> None

    let result = map.insert(1, "updated!!");
    println!("{:?}", map);    // -> {2: "test2", 3: "test3", 1: "updated!!"}
    println!("{:?}", result); // -> Some("test")
}

remove

指定したキーの要素が存在する場合はその要素を削除する。

fn sample() {
    let mut map = HashMap::from([(1, "test"), (2, "test2")]);

    let result = map.remove(&2);
    println!("{:?}", map);    // -> {1: "test"}
    println!("{:?}", result); // -> Some("test2")

    let result = map.remove(&5);
    println!("{:?}", map);    // -> {1: "test"}
    println!("{:?}", result); // -> None
}

clear

すべての要素を削除する。

fn sample() {
    let mut map = HashMap::from([(1, "test"), (2, "test2")]);
    map.clear();
    println!("{:?}", map);
    // -> {}
}

is_empty

要素がひとつもない場合にtrue、ひとつでもある場合はfalseを返す。

fn sample() {
    let mut map = HashMap::from([(1, "test"), (2, "test2")]);

    println!("{:?}", map.is_empty());
    // -> false

    map.clear();
    println!("{:?}", map.is_empty());
    // -> true
}

contains_key

指定したキーが要素に含まれる場合にtrue、含まれない場合にfalseを返す。

fn sample() {
    let map = HashMap::from([(1, "test"), (2, "test2")]);
    println!("{:?}", map.contains_key(&1)); // -> true
    println!("{:?}", map.contains_key(&5)); // -> false
}

drain

すべての要素をHashMapから削除して、削除した要素へのイテレータを返す。

fn sample() {
    let mut map = HashMap::from([(1, "test"), (2, "test2")]);

    let iter = map.drain();
    for v in iter {
        println!("{:?}", v)
        // -> (2, "test2")
        // -> (1, "test")
    }
}

retain

引数に指定した関数がtrueを返す要素のみ残す。

fn sample() {
    let mut map = HashMap::from([
        (1, "test"),
        (2, "test2"),
        (3, "test3"),
        (4, "test4"),
        (5, "test5"),
    ]);

    map.retain(|key, value| key % 2 == 0);
    println!("{:?}", map);
    // -> {2: "test2", 4: "test4"}
}

keys / values

要素のキー、値をそれぞれ取得する。

fn sample() {
    let map = HashMap::from([
        (1, "test"),
        (2, "test2"),
        (3, "test3"),
        (4, "test4"),
        (5, "test5"),
    ]);

    println!("{:?}", map.keys());
    // -> [1, 5, 3, 2, 4]

    println!("{:?}", map.values());
    // -> ["test4", "test3", "test2", "test5", "test"]
}

entry

指定したキーのEntry型を取得する。

Entryでは、「指定した要素が存在する場合」「指定した要素が存在しない場合の処理」といった操作をメソッドチェーンで行うことができる。

fn sample() {
    let mut map = HashMap::from([(1, "test"), (2, "test2")]);

    // 指定したキーの要素がすでに存在する場合はinsertしない
    map.entry(1).or_insert("duplicated");
    println!("{:?}", map);  // -> {1: "test", 2: "test2"}

    // 指定したキーの要素が存在しない場合はinsertする
    map.entry(3).or_insert("inserted");
    println!("{:?}", map);  // -> {2: "test2", 3: "inserted", 1: "test"}
}
参考リンク

最後に

HashMapはやはり使いやすいデータ構造だと思います。

基本的に他プログラミング言語と使用感も変わらないので別言語の経験があればその知識をほとんどそのまま使えます。

しかし、RustのHashMapでは挿入順が保証されないのには注意したいところです。

よかったらシェアしてね!
  • URLをコピーしました!
目次