简体   繁体   English

如何有效地从 HashMap 中查找和插入?

[英]How to lookup from and insert into a HashMap efficiently?

I'd like to do the following:我想做以下事情:

  • Lookup a Vec for a certain key, and store it for later use.查找某个密钥的Vec ,并将其存储以备后用。
  • If it doesn't exist, create an empty Vec for the key, but still keep it in the variable.如果它不存在,则为键创建一个空的Vec ,但仍将其保留在变量中。

How to do this efficiently?如何有效地做到这一点? Naturally I thought I could use match :当然,我认为我可以使用match

use std::collections::HashMap;

// This code doesn't compile.
let mut map = HashMap::new();
let key = "foo";
let values: &Vec<isize> = match map.get(key) {
    Some(v) => v,
    None => {
        let default: Vec<isize> = Vec::new();
        map.insert(key, default);
        &default
    }
};

When I tried it, it gave me errors like:当我尝试它时,它给了我以下错误:

error[E0502]: cannot borrow `map` as mutable because it is also borrowed as immutable
  --> src/main.rs:11:13
   |
7  |     let values: &Vec<isize> = match map.get(key) {
   |                                     --- immutable borrow occurs here
...
11 |             map.insert(key, default);
   |             ^^^ mutable borrow occurs here
...
15 | }
   | - immutable borrow ends here

I ended up with doing something like this, but I don't like the fact that it performs the lookup twice ( map.contains_key and map.get ):我最终做了这样的事情,但我不喜欢它执行两次查找( map.contains_keymap.get )的事实:

// This code does compile.
let mut map = HashMap::new();
let key = "foo";
if !map.contains_key(key) {
    let default: Vec<isize> = Vec::new();
    map.insert(key, default);
}
let values: &Vec<isize> = match map.get(key) {
    Some(v) => v,
    None => {
        panic!("impossiburu!");
    }
};

Is there a safe way to do this with just one match ?有没有一种安全的方法可以只用一场match来做到这一点?

The entry API is designed for this. entry API就是为此而设计的。 In manual form, it might look like在手动形式中,它可能看起来像

use std::collections::hash_map::Entry;

let values: &Vec<isize> = match map.entry(key) {
    Entry::Occupied(o) => o.into_mut(),
    Entry::Vacant(v) => v.insert(default)
};

Or one can use the briefer form:或者可以使用更简短的形式:

map.entry(key).or_insert_with(|| default)

If default is OK/cheap to compute even when it isn't inserted, it can also just be:如果即使没有插入default也可以计算/便宜,它也可以是:

map.entry(key).or_insert(default)

I've used @huon and @Shepmaster's answer and just implemented it as a trait:我使用了@huon 和@Shepmaster 的答案,并将其作为一个特征来实现:

use std::collections::HashMap;
use std::hash::Hash;

pub trait InsertOrGet<K: Eq + Hash, V: Default> {
    fn insert_or_get(&mut self, item: K) -> &mut V;
}

impl<K: Eq + Hash, V: Default> InsertOrGet<K, V> for HashMap<K, V> {
    fn insert_or_get(&mut self, item: K) -> &mut V {
        return match self.entry(item) {
            std::collections::hash_map::Entry::Occupied(o) => o.into_mut(),
            std::collections::hash_map::Entry::Vacant(v) => v.insert(V::default()),
        };
    }
}

Then somewhere else I can just do:然后在其他地方我可以做:

use crate::utils::hashmap::InsertOrGet;

let new_or_existing_value: &mut ValueType = my_map.insert_or_get(my_key.clone());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM