Як шукати і ефективно вставляти в HashMap?


102

Я хотів би зробити наступне:

  • Знайдіть Vecпевний ключ і зберігайте його для подальшого використання.
  • Якщо його немає, створіть порожній Vecдля ключа, але все ж зберігайте його в змінній.

Як це зробити ефективно? Природно, я думав, що можу використовувати match:

use std::collections::HashMap;

// This code doesn't compile.
let mut map = HashMap::new();
let key = "foo";
let values: &Vec<isize> = match map.get(key) {
    Some(v) => v,
    None => {
        let default: Vec<isize> = Vec::new();
        map.insert(key, default);
        &default
    }
};

Коли я спробував це, він давав мені помилки, як:

error[E0502]: cannot borrow `map` as mutable because it is also borrowed as immutable
  --> src/main.rs:11:13
   |
7  |     let values: &Vec<isize> = match map.get(key) {
   |                                     --- immutable borrow occurs here
...
11 |             map.insert(key, default);
   |             ^^^ mutable borrow occurs here
...
15 | }
   | - immutable borrow ends here

Я закінчив робити щось подібне, але мені не подобається те, що він виконує пошук двічі ( map.contains_keyі map.get):

// This code does compile.
let mut map = HashMap::new();
let key = "foo";
if !map.contains_key(key) {
    let default: Vec<isize> = Vec::new();
    map.insert(key, default);
}
let values: &Vec<isize> = match map.get(key) {
    Some(v) => v,
    None => {
        panic!("impossiburu!");
    }
};

Чи є безпечний спосіб зробити це лише одним match?

Відповіді:


119

entryAPI призначений для цього. У ручній формі це може виглядати так

use std::collections::hash_map::Entry;

let values: &Vec<isize> = match map.entry(key) {
    Entry::Occupied(o) => o.into_mut(),
    Entry::Vacant(v) => v.insert(default)
};

Або ви можете скористатися формою більш швидкої:

map.entry(key).or_insert_with(|| default)

Якщо defaultнормально / дешево обчислювати, навіть якщо він не вставлений, він також може бути:

map.entry(key).or_insert(default)

Дякуємо за швидкий ансер! Тепер я дізнався, що я повинен заглянути трохи вглиб документів.
Юсуке Шиняма

22
Проблема з entry () полягає в тому, що вам завжди доведеться клонувати ключ, чи є спосіб уникнути цього?
Паскалій

@Pascalius ви можете ввести свій тип ключа &T(якщо ключі переживають карту, наприклад, статичні рядки) або Rc<T>замість цього T- але це не дуже добре в будь-якому випадку
kbolino

@Pascalius: ви можете використовувати v.key()в виразі для default, і тоді він отримає посилання на ключ, як він існує в хешмапі, тому ви можете уникнути клонування таким чином
Кріс Бек,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.