Rust与HashMap

简介: Rust与HashMap

HashMap

类似于其它语言的字典,JavaScript中的Map。Rust中的HashMap是键值对的集合,并且它是同构的,本篇文章将对Rust中的HashMap进行介绍。

HashMap\<K,V>

以键值对的形式存储数据,一个key对应一个value

Hash函数:决定如何在内存中存放K和V

适用场景:通过K(任何类型)来寻找数据,而不是通过索引

创建HashMap

创建空HashMap:使用new()函数

use std::collections::HashMap;//需要导入

fn main() {
    let mut hm: HashMap<String, i32> = HashMap::new();
}

并且使用insert()方法,添加数据:

let mut hm = HashMap::new();
hm.insert(String::from("yellow"), 8);

HashMap用得较少,所以不在预导入模块中

标准库对其支持比较少,没有内置的宏来创建HashMap

HashMap的数据存储在heap上,并且是同构的。

也就是说,一个HashMap中:

  • 所有的K必须是同一种类型
  • 所有的V必须是同一种类型

使用collect方法创建HashMap

使用条件比较特殊:在元素类型为Tuple的Vector上使用collect方法,可以组建一个HashMap。

  • 要求Tuple有两个值:一个作为K,一个作为V
  • collect方法可以把数据整合成很多集合类型,包括HashMap。所以返回值得显示指明类型。
let teams = vec![String::from("white"), String::from("black")];
let intital_scores = vec![10, 50];

let scores: HashMap<_, _> = teams.iter().zip(intital_scores.iter()).collect();
println!("{:?}", scores); //{"white": 10, "black": 50}

其中:

  • zip()接受一个参数,将调用者中的元素与参数中的元素一一对应组成Tuple(元组),若数量不匹配,多的元素会丢掉
  • 注意声明类型,上面例子的HashMap中的数据类型我们使用_代替,rust编译器会根据后续操作自动识别类型。

HashMap和所有权

对于实现了Copy trait的类型如i32,值会被复制到HashMap

对于拥有所有权的值如String,值会被移动,所有权会被转移给HashMap

let file_type = String::from("word");
let file_name = String::from("about_me");

let mut map = HashMap::new();
map.insert(file_type, file_name);
// println!("{},{}", file_name, file_type); //borrow of moved value: `file_type`(被移动了)
println!("{:?}", map); //{"word": "about_me"}

如果将值的引用插入到HashMap,值的本身不会被移动

let file_type = String::from("word");
let file_name = String::from("about_me");

let mut map = HashMap::new();
map.insert(&file_type, &file_name);
println!("{},{}", file_type, file_name); //word,about_me
println!("{:?}", map); //{"word": "about_me"}

但是,在HashMap有效期内,被引用的值必须保持有效

访问HashMap中的值

使用get方法:

  • 参数:K
  • 返回值:Option<&V>
let mut map = HashMap::new();
map.insert(String::from("white"), 10);
map.insert(String::from("black"), 20);

let team_name = String::from("white");
let team_score = map.get(&team_name);

match team_score {
    Some(s) => println!("{}", s),
    None => println!("team is not exit"),
}
//10

遍历HashMap

使用for循环

let mut map = HashMap::new();
map.insert(String::from("white"), 10);
map.insert(String::from("black"), 20);

for (k, v) in &map {
    println!("{}:{}", k, v);
}
// white:10
// black:20

更新HashMap\<K,V>

HashMap的大小可变,每个Key只能对应一个Value

更新HashMap中的数据时,我们可能遇到以下的情况:

  • K已经存在,并且对应了一个V:
    • 替换现有的V
    • 保留现有的V,忽略新的V
    • 合并现有的V和新的V
  • K不存在
    • 添加一对K,V

替换现有的V

如果向HashMap插入一对KV,然后再插入同样的K,但是不同的V,那么原来的V会被替换掉:

let mut map = HashMap::new();
map.insert(String::from("white"), 10);
map.insert(String::from("white"), 20);
println!("{:?}", map); //{"white": 20}

只在K不对应任何值时,才插入V

针对这种情况,我们需要使用entry方法:检查指定的K是否对应一个V:

  • 参数为K
  • 返回enum Entry:代表值是否存在
let mut map = HashMap::new();
map.insert(String::from("white"), 10);

let e = map.entry(String::from("black"));
println!("{:?}", e); //Entry(VacantEntry("black"))
e.or_insert(20);

map.entry(String::from("white")).or_insert(25);
println!("{:?}", map); //{"white": 10, "black": 20}

我们可以看到第5行打印Entry(VacantEntry("black")),这表示Entry枚举中black不存在。所以我们可以使用`or_insert()成功插入V

对于Entry的or_insert()方法:

  • 返回:
    • 如果K存在,返回对于的V的一个可变引用
    • 如果K不存在,将方法参数作为K的新值插入,返回这个这的可变引用

基于现有V来更新V

let text = "nice demo yeah yeah yeah";

let mut map = HashMap::new();

for word in text.split_whitespace() {
    let count = map.entry(word).or_insert(0);
    *count += 1;
}
println!("{:#?}", map);
// {
//     "nice": 1,
//     "demo": 1,
//     "yeah": 3,
// }

其中,&str.split_whitespace()方法将字符串切片以空格分割,并且返回一个迭代器。

count是一个可变引用,如果HashMap中有对应的值map.entry(word).or_insert(0),返回值的可变引用,如果没有,返回0的可变引用。

我们使用*来解引用,从而改变可变引用的值。

我们最后看到,nice出现一次,demo出现1次,yeah出现3次。

Hash函数

默认情况下,HashMap使用加密功能强大的Hash函数,可以抵抗拒绝服务(Dos)攻击。关于Hash函数,我们需要知道:

  • 不是可用的最快的Hash算法
  • 但具有更好的安全性

我们可以指定不同的hasher来切换到另一个函数(hasher是实现BuildHasher trait的类型)

相关文章
|
28天前
|
存储 Rust API
30天拿下Rust之HashMap
30天拿下Rust之HashMap
12 0
|
4月前
|
存储 Rust
Rust HashMap详解及单词统计示例
Rust HashMap详解及单词统计示例
|
5月前
|
存储 缓存 Rust
Rust 笔记:Rust 语言中哈希结构(哈希映射,HashMap)、集合(哈希集,HashSet)及其使用
Rust 笔记:Rust 语言中哈希结构(哈希映射,HashMap)、集合(哈希集,HashSet)及其使用
900 0
|
存储 缓存 Rust
Rust 笔记:Rust 语言中映射(HashMap)与集合(HashSet)及其用法
本文介绍 Rust 中哈希结构相关概念及其使用。在 Rust 中,提供了两种哈希表,一个是 HashMap,另外一个是 HashSet,本文都将逐一介绍,并介绍 哈希函数 的用法。
296 0
|
8天前
|
Java
让星星⭐月亮告诉你,HashMap中保证红黑树根节点一定是对应链表头节点moveRootToFront()方法源码解读
当红黑树的根节点不是其对应链表的头节点时,通过调整指针的方式将其移动至链表头部。具体步骤包括:从链表中移除根节点,更新根节点及其前后节点的指针,确保根节点成为新的头节点,并保持链表结构的完整性。此过程在Java的`HashMap$TreeNode.moveRootToFront()`方法中实现,确保了高效的数据访问与管理。
19 2
|
8天前
|
Java 索引
让星星⭐月亮告诉你,HashMap之往红黑树添加元素-putTreeVal方法源码解读
本文详细解析了Java `HashMap` 中 `putTreeVal` 方法的源码,该方法用于在红黑树中添加元素。当数组索引位置已存在红黑树类型的元素时,会调用此方法。具体步骤包括:从根节点开始遍历红黑树,找到合适位置插入新元素,调整节点指针,保持红黑树平衡,并确保根节点是链表头节点。通过源码解析,帮助读者深入理解 `HashMap` 的内部实现机制。
20 2
|
10天前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
24 0
|
8天前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
29 5
|
8天前
|
算法 索引
让星星⭐月亮告诉你,HashMap的resize()即扩容方法源码解读(已重新完善,如有不足之处,欢迎指正~)
`HashMap`的`resize()`方法主要用于数组扩容,包括初始化或加倍数组容量。该方法首先计算新的数组容量和扩容阈值,然后创建新数组。接着,旧数组中的数据根据`(e.hash & oldCap)`是否等于0被重新分配到新数组中,分为低位区和高位区两个链表,确保数据迁移时的正确性和高效性。
31 3
|
8天前
|
Java 索引
让星星⭐月亮告诉你,HashMap中红黑树TreeNode的split方法源码解读
本文详细解析了Java中`HashMap`的`TreeNode`类的`split`方法,该方法主要用于在`HashMap`扩容时将红黑树节点从旧数组迁移到新数组,并根据`(e.hash & oldCap)`的结果将节点分为低位和高位两个子树。低位子树如果元素数少于等于6,则进行去树化操作;若多于6且高位子树非空,则进行树化操作,确保数据结构的高效性。文中还介绍了`untreeify`和`replacementNode`方法,分别用于将红黑树节点转换为普通链表节点。
15 2