Hashmaps
Hashmap<K, V>
Это изменяемая структура словарь (“dictionary” в Python), которая хранит пары “ключ->значение”. В Rust Prelude она не входит, макроса создания не имеет. Поэтому нужно указывать библиотеку явно и явно создавать структуру.
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Alpha"),1);
scores.insert(String::from("Beta"),2);
scores.insert(String::from("Gamma"),3);
}
Все ключи Hashmap должны быть уникальны и одного типа, все значения должны быть одного типа.
Warning
Значения с кучи типа String перемещаются (move) в Hashmap, который становится их владельцем.
Взятие значения по ключу из Hashmap с помощью get
нужно сопровождать проверкой - есть ли в памяти запрашиваемые ключ и значение:
let name = String::from("Gamma");
if let Some(letter_num) = scores.get(&name) {
println!("{}",letter_num);
} else { println!("Value not in HashMap!"); }
Итерация по Hashmap похожа на итерацию по вектору:
for (key, value) in &scores {
println!("{key} -> {value}"); }
Обновление Hashmap
Есть ряд стратегий обновления значений в Hashmap:
- Перезаписывать всегда
scores.insert(String::from("Gamma"),3); // вставка дважды значений по одному
scores.insert(String::from("Gamma"),6); // ключу сохранит последнее значение
- Записывать значение, если у ключа его нет:
scores.entry(String::from("Delta")).or_insert(4); // entry проверяет наличие
// значения, or_insert возвращает mut ссылку на него, либо записывает новое
// значение и возвращает mut ссылку на это значение.
- Записывать значение, если ключа нет. Если же у ключа есть значение, модифицировать его:
use std::collections::HashMap;
let mut map: HashMap<&str, u32> = HashMap::new();
map.entry("poneyland") // первое добавление
.and_modify(|e| { *e += 1 })
.or_insert(42); // добавит ключ "poneyland: 42"
assert_eq!(map["poneyland"], 42);
map.entry("poneyland") // второе добавление найдёт ключ со значением
.and_modify(|e| { *e += 1 }) // и модифицирует его
.or_insert(42);
assert_eq!(map["poneyland"], 43);
- Записывать значение, если ключа нет, в виде результата функции. Эта функция получает ссылку на значение ключа key, который передаётся в .entry(key):
use std::collections::HashMap;
let mut map: HashMap<&str, usize> = HashMap::new();
map
.entry("poneyland")
.or_insert_with_key(|key| key.chars().count());
assert_eq!(map["poneyland"], 9);
- Поднимать старое значение ключа, проверять его перед перезаписью:
let text = "hello world wonderful world";
let mut map = HashMap::new();
for word in text.split_whitespace() {
let count = map.entry(word).or_insert(0);
*count += 1;
}
println!("{:?}",map); // {"wonderful": 1, "hello": 1, "world": 2}
Инициализация HashMap со значениями по-умолчанию
Поведение аналогично типу defaultdict
в Python. Заполнять ключами HashMap:
- в случае отсутствия ключа, создавать его со значением по-умолчанию (0);
- в случае присутствия ключа, добавлять к значению +1.
use std::collections::HashMap;
pub fn main() {
let num_vec = vec![1, 2, 1, 3, 5, 2, 1, 4, 6];
let mut number_count: HashMap<i32, i32> = HashMap::new();
for key in num_vec {
*number_count.entry(key).or_default() += 1;
}
for (k, v) in number_count {
print!("{} -> {}; ", k, v);
}
}
// 4 -> 1; 1 -> 3; 2 -> 2; 6 -> 1; 5 -> 1; 3 -> 1;