When working with HashMap or BTreeMap, use the Entry API to perform insert-or-update operations in a single lookup. Avoiding patterns like contains_key followed by insert or get followed by insert reduces the number of hash computations and lookups from two (or more) down to one, improving performance and making the code more idiomatic.
Common Patterns
Incrementing a value
Instead of checking for existence and then inserting, use .or_insert(0) to ensure a value exists before modifying it.
Grouping items
Use .or_default() to initialize a collection (like a Vec) for a key if it doesn't exist, then push the item into it.
Complex conditional updates
For logic that requires checking the existing value before deciding whether to update or insert, use a match statement on map.entry(key) to handle Entry::Occupied and Entry::Vacant cases separately.
use std::collections::HashMap;
use std::collections::hash_map::Entry;
// Incrementing a value
fn increment(map: &mut HashMap<String, u32>, key: String) {
*map.entry(key).or_insert(0) += 1;
}
// Grouping items
fn group_by_category(items: Vec<Item>) -> HashMap<Category, Vec<Item>> {
let mut groups: HashMap<Category, Vec<Item>> = HashMap::new();
for item in items {
groups.entry(item.category.clone())
.or_default()
.push(item);
}
groups
}
// Complex conditional updates
match map.entry(key) {
Entry::Occupied(mut entry) => {
let value = entry.get_mut();
if should_update(value) {
*value = new_value;
}
}
Entry::Vacant(entry) => {
entry.insert(default_value);
}
}