Come cercare e inserire in una HashMap in modo efficiente?


102

Mi piacerebbe fare quanto segue:

  • Cerca una Vecdeterminata chiave e conservala per un uso successivo.
  • Se non esiste, crea un vuoto Vecper la chiave, ma tienilo comunque nella variabile.

Come farlo in modo efficiente? Naturalmente ho pensato di poter usare 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
    }
};

Quando l'ho provato, mi ha dato errori come:

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

Ho finito per fare qualcosa di simile, ma non mi piace il fatto che esegua la ricerca due volte ( map.contains_keye 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!");
    }
};

C'è un modo sicuro per farlo con uno solo match?

Risposte:


120

L' entryAPI è progettata per questo. In forma manuale, potrebbe sembrare

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)
};

Oppure si può usare la forma più breve:

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

Se defaultè OK / economico da calcolare anche quando non è inserito, può anche essere solo:

map.entry(key).or_insert(default)

Grazie per una rapida risposta! Ora ho imparato che dovrei esaminare un po 'più a fondo i documenti.
Yusuke Shinyama

22
il problema con entry () è che devi sempre clonare la chiave, c'è un modo per evitarlo?
Pascalius

@Pascalius potresti creare il tuo tipo di chiave &T(se le chiavi sopravvivono alla mappa, ad esempio stringhe statiche) o Rc<T>invece di T- ma non è carino in entrambi i casi
kbolino

@Pascalius: puoi usare v.key()nell'espressione per default, e quindi otterrà un riferimento alla chiave così come esiste nella hashmap, quindi potresti evitare un clone in questo modo
Chris Beck
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.