我必须立刻押注 Rust 之十一🎲:HashMap

320 阅读4分钟

基础概念

HashMap 是 Rust 标准库中的一个集合类型,它是一个基于哈希表实现的键值对映射容器。HashMap 允许你根据键(key)快速查找和修改对应的值(value)。它在 Rust 中广泛用于存储具有唯一键的关联数据。

  • 键值对存储HashMap 存储的是一组键值对,每个键(key)是唯一的,值(value)可以是任何类型。
  • 无序性HashMap 是无序的,即它的元素是根据哈希函数来存储的,因此无法保证插入的顺序与遍历的顺序相同。
  • 哈希函数HashMap 使用哈希函数将键映射到特定的位置,这使得查找操作非常高效。默认情况下,Rust 使用 SipHash 哈希算法。

创建 HashMap

你可以通过以下方式创建 HashMap

use std::collections::HashMap;

fn main() {
    // 创建一个空的 HashMap
    let mut map = HashMap::new();

    // 使用 `insert` 添加元素
    map.insert("apple", 3);
    map.insert("banana", 5);

    // from 函数
    let mut map = HashMap::from([("a", 1), ("b", 2)]);

    println!("{:?}", map);  // 输出: {"a": 1, "b": 2}
}

常用操作与方法

添加键值对

  • insert(key, value):插入一个新的键值对。如果键已存在,返回旧的值(如果有的话)。
    let mut map = HashMap::new();
    map.insert("key1", 100);
    map.insert("key2", 200);
    
    println!("{:?}", map);  // 输出: {"key1": 100, "key2": 200}
    

获取值

  • get(&key):根据键获取值,返回 Option<&V> 类型。如果键不存在,返回 None

    let mut map = HashMap::new();
    map.insert("apple", 3);
    map.insert("banana", 5);
    
    if let Some(&value) = map.get("apple") {
        println!("The value for 'apple' is {}", value);  // 输出: 3
    }
    
  • contains_key(&key):检查某个键是否存在于 HashMap 中。

    let mut map = HashMap::new();
    map.insert("apple", 3);
    map.insert("banana", 5);
    
    if map.contains_key("banana") {
        println!("Found 'banana' in the map");
    }
    

删除键值对

  • remove(&key):根据键删除对应的键值对,返回被删除的值(如果有的话)。
    let mut map = HashMap::new();
    map.insert("apple", 3);
    map.insert("banana", 5);
    
    map.remove("apple");
    println!("{:?}", map);  // 输出: {"banana": 5}
    

清空所有元素

  • clear():删除所有的键值对,但不改变 HashMap 的容量。
    let mut map = HashMap::new();
    map.insert("apple", 3);
    map.insert("banana", 5);
    
    map.clear();
    println!("{:?}", map);  // 输出: {}
    

遍历 HashMap

你可以使用 for 循环来遍历 HashMap 中的所有键值对。注意,HashMap 的元素是以 (&K, &V) 形式提供的引用。

let mut map = HashMap::new();
map.insert("apple", 3);
map.insert("banana", 5);

for (key, value) in &map {
    println!("{}: {}", key, value);
}
// 输出:
// apple: 3
// banana: 5

获取所有键和值

  • keys():获取所有的键。
  • values():获取所有的值。
let mut map = HashMap::new();
map.insert("apple", 3);
map.insert("banana", 5);

// 获取所有键
for key in map.keys() {
    println!("{}", key);  // 输出: apple banana
}

// 获取所有值
for value in map.values() {
    println!("{}", value);  // 输出: 3 5
}

获取键值对的可变引用

  • entry(key):提供一种 API,允许你检查和操作 HashMap 中某个键的值。如果键不存在,可以插入一个默认值。
let mut map = HashMap::new();
map.insert("apple", 3);
map.insert("banana", 5);

map.entry("apple").or_insert(10);  // 如果 "apple" 已经存在,就不插入新值
map.entry("pear").or_insert(4);  // 如果 "pear" 不存在,就插入 "pear": 4

println!("{:?}", map);  // 输出: {"apple": 3, "banana": 5, "pear": 4}

按键访问和修改

  • get_mut(&key):获取某个键对应的可变引用,允许修改该键的值。
let mut map = HashMap::new();
map.insert("apple", 3);

if let Some(value) = map.get_mut("apple") {
    *value = 10;
}

println!("{:?}", map);  // 输出: {"apple": 10}

内存管理与性能

容量与负载因子

  • 容量(Capacity)HashMap 会根据实际需要自动调整容量,以避免频繁地重新分配内存。通常,HashMap 会在元素增加时动态扩容,以提高性能。
  • 负载因子(Load Factor)HashMap 的负载因子通常设定为 0.75,即当元素数量超过容量的 75% 时,HashMap 会进行扩容。

扩容策略

HashMap 的元素超过当前容量时,它会重新分配内存,通常会将容量扩展到原来的两倍。这是为了保持哈希表查找操作的平均时间复杂度为 O(1)。

use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.insert("apple", 3);
    map.insert("banana", 5);
    map.reserve(10);  // 为了避免后续频繁扩容,预先分配更多的内存

    println!("Capacity after reserve: {}", map.capacity());
}

重要特点

键值对的不可重复性

  • HashMap 要求键必须是唯一的。如果你插入一个已有键的键值对,新的值会覆盖旧的值。
let mut map = HashMap::new();
map.insert("apple", 3);
map.insert("apple", 10);  // 覆盖 "apple" 对应的值

println!("{:?}", map);  // 输出: {"apple": 10}

HashMap 的顺序不保证

HashMap 的元素存储是无序的,因此不能依赖插入顺序。

let mut map = HashMap::new();
map.insert("apple", 3);
map.insert("banana", 5);
map.insert("cherry", 7);

for (key, value) in &map {
    println!("{}: {}", key, value);
}
// 输出顺序可能为:
// cherry: 7
// banana: 5
// apple: 3

应用场景

存储和查询关联数据:当你需要根据键快速查找对应的值时,HashMap 是一个非常理想的选择。