HashMap::contains_key() / remove() / entry()
| 対応: | Rust 1.0(2015) |
|---|
RustのHashMapには、キーの存在を確認する『contains_key()』、エントリを削除する『remove()』、そして値がなければ追加する『entry().or_insert()』が用意されています。
構文
use std::collections::HashMap;
// キーが存在するか確認します(bool を返します)。
let exists = map.contains_key("黒閃");
// キーとその値を削除します(Option<V> を返します)。
let removed = map.remove("黒閃"); // Some(値) または None
// entry API: キーが存在しなければ挿入します。
map.entry("key".to_string()).or_insert(0);
// entry API: 存在する値を更新します。
let count = map.entry("key".to_string()).or_insert(0);
*count += 1; // 値をインクリメントします。
メソッド一覧
| メソッド | 概要 |
|---|---|
| map.contains_key(&key) | キーが存在すればtrueを返します。 |
| map.remove(&key) | キーを削除し、存在した場合は値をSomeで返します。 |
| map.remove_entry(&key) | キーと値のペアをタプルとして返しながら削除します。 |
| map.entry(key) | キーのエントリを返します(OccupiedEntry または VacantEntry)。 |
| .or_insert(value) | エントリが存在しなければ値を挿入し、参照を返します。 |
| .or_insert_with(|| ...) | エントリが存在しなければクロージャの戻り値を挿入します。 |
| .or_default() | エントリが存在しなければデフォルト値(Default::default())を挿入します。 |
| .and_modify(|v| ...) | エントリが存在する場合のみ値を変更します。 |
| map.retain(|k, v| ...) | 条件を満たすエントリのみを残します。 |
サンプルコード
『contains_key』『remove』『retain』の動作を確認するサンプルコードです。
sample_hashmap_contains_key_remove.rs
use std::collections::HashMap;
fn main() {
let mut map: HashMap<String, i32> = HashMap::new();
map.insert("黒閃".to_string(), 3);
map.insert("領域展開".to_string(), 5);
map.insert("反転術式".to_string(), 2);
// contains_key() でキーの存在を確認します。
println!("黒閃 あり?: {}", map.contains_key("黒閃")); // true
println!("呪力操作 あり?: {}", map.contains_key("呪力操作")); // false
// remove() でエントリを削除します。
let removed = map.remove("領域展開");
println!("削除した値: {:?}", removed); // Some(5)
println!("削除後の長さ: {}", map.len()); // 2
let none = map.remove("呪力操作"); // 存在しないキー
println!("存在しないキーを削除: {:?}", none); // None
// --- entry API ---
// 呪術師の術式使用回数をカウントします(典型的なentry活用例)。
let cursed_techniques = "領域展開 反転術式 領域展開 黒閃 反転術式 黒閃 黒閃";
let mut counts: HashMap<String, i32> = HashMap::new();
for technique in cursed_techniques.split_whitespace() {
let count = counts.entry(technique.to_string()).or_insert(0);
*count += 1; // 参照を通じて値をインクリメントします。
}
println!("術式カウント: {:?}", counts);
// or_default() で Default trait のデフォルト値を使います。
let mut vmap: HashMap<String, Vec<i32>> = HashMap::new();
vmap.entry("evens".to_string()).or_default().push(2);
vmap.entry("evens".to_string()).or_default().push(4);
vmap.entry("odds".to_string()).or_default().push(1);
println!("グループ: {:?}", vmap);
// and_modify() で既存の値だけを変更します。
let mut scores: HashMap<String, i32> = HashMap::new();
scores.insert("五条悟".to_string(), 90);
scores.entry("五条悟".to_string()).and_modify(|s| *s += 10);
scores.entry("両面宿儺".to_string()).and_modify(|s| *s += 10).or_insert(0);
println!("scores: {:?}", scores); // 五条悟は100、両面宿儺は0
// retain() で条件を満たすエントリのみ残します。
let mut m: HashMap<&str, i32> = [("a", 1), ("b", 2), ("c", 3), ("d", 4)]
.iter().cloned().collect();
m.retain(|_, v| *v >= 3); // 値が3以上のエントリだけ残します。
println!("retain(value >= 3): {:?}", m);
}
コンパイルして実行すると次のように出力されます。
rustc hashmap_contains_key_remove.rs
./hashmap_contains_key_remove
黒閃 あり?: true
呪力操作 あり?: false
削除した値: Some(5)
削除後の長さ: 2
存在しないキーを削除: None
術式カウント: {"領域展開": 2, "反転術式": 2, "黒閃": 3}
グループ: {"evens": [2, 4], "odds": [1]}
scores: {"五条悟": 100, "両面宿儺": 0}
retain(value >= 3): {"c": 3, "d": 4}
よくあるミス
よくあるミス1: entry API で借用エラーが発生する
entry() を呼んだあとに同じマップを読み書きしようとすると、借用ルール違反でコンパイルエラーになります。
use std::collections::HashMap;
fn main() {
let mut scores: HashMap<String, i32> = HashMap::new();
scores.insert("五条悟".to_string(), 90);
// コンパイルエラー: entry() で可変借用を保持中に scores を読もうとしている
// let e = scores.entry("五条悟".to_string()).or_insert(0);
// println!("{}", scores.get("五条悟").unwrap()); // NG: 可変借用中
// *e += 1;
}
use std::collections::HashMap;
fn main() {
let mut scores: HashMap<String, i32> = HashMap::new();
scores.insert("五条悟".to_string(), 90);
// OK: entry の可変参照を使い終えてから(スコープ外で)マップを読む
{
let e = scores.entry("五条悟".to_string()).or_insert(0);
*e += 10;
}
println!("{}", scores["五条悟"]); // 100
}
よくあるミス2: remove() した値をキーとして再利用しようとする
『remove()』は所有権を持つ値を返します。String キーを使っている場合、元のキーはすでにマップから移動しているため、再挿入には新しい String を作る必要があります。
use std::collections::HashMap;
fn main() {
let mut map: HashMap<String, i32> = HashMap::new();
let key = "両面宿儺".to_string();
map.insert(key, 99);
// remove() は値を返す(キーは消費される)
let value = map.remove("両面宿儺");
println!("削除: {:?}", value); // Some(99)
// 再挿入するには新しい String を使う
map.insert("両面宿儺".to_string(), 100); // OK
println!("{:?}", map.get("両面宿儺")); // Some(100)
}
よくあるミス3: contains_key() で確認後に get() を呼んで二重検索する
『contains_key()』のあとに『get()』を呼ぶと同じキーを2回検索します。entry() API や get() の戻り値で直接処理する方が効率的です。
use std::collections::HashMap;
fn main() {
let mut map: HashMap<String, i32> = HashMap::new();
map.insert("伏黒恵".to_string(), 80);
// 非効率: 2回検索している
if map.contains_key("伏黒恵") {
let val = map.get("伏黒恵").unwrap(); // 2回目の検索
println!("値: {}", val);
}
}
use std::collections::HashMap;
fn main() {
let mut map: HashMap<String, i32> = HashMap::new();
map.insert("伏黒恵".to_string(), 80);
// 効率的: get() の Option を直接処理する
if let Some(val) = map.get("伏黒恵") {
println!("値: {}", val); // 1回の検索で完結
}
}
概要
entry APIはHashMapの最も慣用的な使い方の一つです。「キーが存在しなければ挿入し、存在すれば更新する」という処理を安全かつ効率的に書けます。単語カウントのような集計処理でよく使われます。
『or_insert()』は値の可変参照(&mut V)を返します。この参照を通じて値を変更できるため、インクリメントや配列への追加が可能です。
HashMapには値を直接検索する『contains_value()』に相当するメソッドはありません。特定の値を持つキーを探すには、『values().any(|v| v == &target)』または『iter().find(|(k, v)| v == &&target)』を使います。
HashMapの生成・基本操作は『HashMap::new() / insert() / get()』を、イテレーションは『HashMap::keys() / values() / iter()』を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。