哈希表非常常用,这篇文章不介绍什么是哈希表,只说下 Rust 标准库中提供的哈希表 HashMap。
Rust 中 HashMap 的声明如下:
1 | pub struct HashMap<K, V, S = RandomState> |
使用哈希表 HashMap 需要先 use std::collections::HashMap;
。
这篇文章只会介绍几个基础用法,用以入门,详细内容请看官方文档 HashMap in std::collections::hash_map - Rust。
1. 新建一个 HashMap
新建 HashMap 要使用 HashMap::new()
函数。
下面看代码:
1 |
|
2. 更新 HashMap
2.1. 插入键值对
插入键值对使用 insert()
函数:
1 | pub fn insert(&mut self, k: K, v: V) -> Option<V> |
insert()
将键值对插入 HashMap。如果 HashMap 没有待插入的键,就插入这个键值对,然后返回 None
;若 HashMap 中已经存在了这个键,则更新其值,并返回旧的值(Option
下面看一个代码示例:
1 |
|
2.2. 根据旧值更新一个值
在哈希表中,根据旧值更新一个值是一个很常用的操作。
我们判断一个键是否在哈希表中,如果在就对其值做某些操作,如果不在就插入一个键值对。
我们先看一段代码:
1 |
|
输出
1 | {"world": 2, "hello": 1, "wonderful": 1} |
上面的代码中用两个你可能没见过的函数:
一个是 text.split_whitespace()
,顾名思义,这个函数把字符串 text
的内容按空格划分开了,这里不详细解释了,这不是这里的重点。另一个是 map.entry(word).or_insert(0);
。
下面先介绍一下 HashMap 的 entry()
函数。
1 | pub fn entry(&mut self, key: K) -> Entry<'_, K, V> |
这个函数获取给定 key 在 HashMap 中的对应条目,你可以对这个条目原地操作,该条目可以是空的,也可以是已有的。
关于这个 Entry
类型,详细请查看文档 Entry in std::collections::hash_map - Rust,这里只说其一个我们用到的函数 or_insert()
。
1 | pub fn or_insert(self, default: V) -> &'a mut V |
如果 key 对应的条目是空的,则在 HashMap 中插入一个键值对,值就是参数 default
,然后返回这个值的可变引用。如果 key 对应的条目不是空的,直接返回这个值的可变引用。
下面通过代码理解一下 entry()
和 or_insert()
:
1 |
|
现在再回头看 let count = map.entry(word).or_insert(0);
这个语句就很明了了:先拿到 map
中键 word
对应的条目,如果这个条目是空的,就插入一个键值对 ("word", 0)
,然后返回这个 0
的可变引用,如果这个条目不是空的,就直接返回这个值的可变引用。然后后面就可以通过这个可变引用来修改值了,注意为了赋值必须首先使用星号(*
)解引用 count
。
2.3. 删除键值对
删除键值对使用 remove()
函数:
1 | pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V> |
从 HashMap 中删除一个键,如果该键在 HashMap 中,则以 Option<T>
类型返回该键的值,否则返回 None
。
下面看一段代码示例:
1 |
|
2.4. 清空 HashMap
1 | pub fn clear(&mut self) |
这个比较简单,不举例子了,注意慎用。
3. 访问 HashMap
3.1. 使用 get()
获取一个 key 对应的值
你可以通过 get()
方法并提供对应的键来从 HashMap 中获取值。
1 | pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> |
示例代码:
1 |
|
get()
会返回 Option<&T>
,如果哈希表中没有其参数提供的键,则返回 None
。
与很多其他集合的 get()
一样,其得到的是不可变引用。想要可变引用使用 get_mut()
,用法一致。
1 | pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V> |
3.2. 遍历整个 HashMap
这个不需要额外的函数,直接从代码理解吧,看下面的 for
循环写法。
1 |
|
输出
1 | Yellow: 50 |
这个 for
循环会以任意顺序打印出 HashMap 中的每一个键值对。
3.3. 检查一个 key 是否存在于 HashMap 中
1 | pub fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool |
示例代码:
1 |
|
3.4. 检查 HashMap 是否为空
1 | pub fn is_empty(&self) -> bool |
HashMap 为空返回 true
,否则返回 false
。比较简单,不举例子了。
3.5. 获取键值对个数
1 | pub fn len(&self) -> usize |
返回类型 uszie
,就是 HashMap 中键值对的数量。
4. 扩展:HashMap 与所有权
对于像 i32
这样的实现了 Copy
trait 的类型,其值可以拷贝进哈希 map。
对于像 String
这样拥有所有权的值,其值将被移动而 HashMap 会成为这些值的所有者。
1 |
|
如果将值的引用插入 HashMap,这些值本身将不会被移动进 HashMap。但是这些引用指向的值必须至少在 HashMap 有效时也是有效的。