Bagaimana cara mencari dan memasukkan ke dalam HashMap secara efisien?


102

Saya ingin melakukan hal berikut:

  • Cari Veckunci tertentu, dan simpan untuk digunakan nanti.
  • Jika tidak ada, buat kosong Vecuntuk kunci tersebut, tetapi tetap simpan di variabel.

Bagaimana melakukan ini secara efisien? Secara alami saya pikir saya bisa menggunakan 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
    }
};

Ketika saya mencobanya, saya mendapatkan kesalahan seperti:

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

Saya akhirnya melakukan sesuatu seperti ini, tetapi saya tidak suka fakta bahwa ia melakukan pencarian dua kali ( map.contains_keydan 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!");
    }
};

Apakah ada cara yang aman untuk melakukan ini hanya dengan satu orang match?

Jawaban:


120

The entryAPI dirancang untuk ini. Dalam bentuk manual, mungkin terlihat seperti ini

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

Atau seseorang dapat menggunakan formulir singkat:

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

Jika defaultOK / murah untuk menghitung bahkan ketika tidak dimasukkan, itu juga bisa menjadi:

map.entry(key).or_insert(default)

Terima kasih atas jawaban yang cepat! Sekarang saya telah belajar bahwa saya harus melihat lebih dalam ke dokumen-dokumen itu.
Yusuke Shinyama

22
Masalah dengan entry () adalah, Anda harus selalu mengkloning kunci, apakah ada cara untuk menghindarinya?
Pascalius

@Pascalius Anda dapat membuat jenis kunci Anda &T(jika kuncinya hidup lebih lama dari peta, mis. String statis) atau Rc<T>alih-alih T- tetapi tidak bagus dalam kedua kasus
kbolino

@Pascalius: Anda dapat menggunakan v.key()ekspresi for default, dan kemudian akan mendapatkan referensi ke kunci seperti yang ada di hashmap, jadi Anda dapat menghindari klon dengan cara ini
Chris Beck
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.