[英]What's the best practice of `insert or update` operation in Rust's HashMap?
I'm looking for a best practice for the insert-or-update
operation, because it is very commonly used, I think we need to optimize both its writing style and efficiency.我正在寻找insert-or-update
操作的最佳实践,因为它很常用,我认为我们需要优化它的写作风格和效率。
Assume the following scenario: I got a hashmap
假设以下场景:我得到一个hashmap
let classes: HashMap(String, HashSet<String>) = HashMap::new()
which is used to store students and whos classes.用于存储学生和谁的班级。
The data is expected to be in the following form:数据预计为以下形式:
{ key: "ClassA", value: {"Bob", "Marry", "Jack"}},
{ key: "ClassB", value: {"Lee", "Tom"}},
Now I got a new set of data of a student and his/her class, let's take:现在我得到了一个学生和他/她的 class 的一组新数据,让我们来看看:
{ name: "Alice", class: "ClassC"}
Since I'm not sure if the class already appears in the classes
HashMap, I need to figure out whether it exists first, if it is, I'm going to update the value, and if it's not, I'm going to add a new key->value
pair.由于我不确定class是否已经出现在classes
HashMap中,我需要先弄清楚它是否存在,如果存在,我将更新该值,如果不存在,我将添加一个新的key->value
对。
What's the correct way to do it with out any unnecessary move or copy?没有任何不必要的移动或复制的正确方法是什么? Based on other answers, I tried to use std::collections::hash_map::Entry
but I failed.根据其他答案,我尝试使用std::collections::hash_map::Entry
但我失败了。
Thanks!谢谢!
The idiomatic way to use that map would be something like this:使用 map 的惯用方式是这样的:
use std::collections::HashSet;
use std::collections::HashMap;
fn main() {
let mut classes: HashMap<String, HashSet<String>> = HashMap::new();
let e = classes.entry(String::from("ClassA"));
e.or_default().insert(String::from("Alice"));
let e = classes.entry(String::from("ClassA"));
e.or_default().insert(String::from("Bob"));
dbg!(&classes);
}
The HashMap::entry()
function returns an Entry
value, that represents either the value contained in the map or the place it would be if it were in the map. The HashMap::entry()
function returns an Entry
value, that represents either the value contained in the map or the place it would be if it were in the map. This Entry
type has a lot of functions to access the contained value, and create it if needed.这个Entry
类型有很多函数可以访问包含的值,并在需要时创建它。 In your case the easiest function to use is or_default
that creates a default value (an empty set) if the value is not yet in the map.在您的情况下,最容易使用的 function 是or_default
,如果 map 中还没有该值,它会创建一个默认值(一个空集)。
Then, since you have a mutable reference to the set inside the map, just insert the desired value.然后,由于您对 map 内的集合有一个可变引用,因此只需插入所需的值。
To get an insert or update behavior I would do it in this fluent way要获得插入或更新行为,我会以这种流畅的方式进行
classes.entry(String::from("ClassC"))
.or_default()
.insert(String::from("Alice"));
A test example which covers your scenario涵盖您的场景的测试示例
#[test]
fn test(){
// Arrange
let mut classes: HashMap<String, HashSet<String>> = HashMap::from([
(String::from("ClassA"), HashSet::from([String::from("Bob"), String::from("Marry"), String::from("Jack")])),
(String::from("ClassB"), HashSet::from([String::from("Lee"), String::from("Tom")])),
]);
let class_c = String::from("ClassC");
let alice = String::from("Alice");
// Act
classes.entry(class_c.clone())
.or_default()
.insert(alice.clone());
// Assert
let contains_class = classes.contains_key(&class_c);
let contains_alice = classes.get(&class_c)
.unwrap().contains(&alice);
assert_eq!(true, contains_class);
assert_eq!(true, contains_alice);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.