Làm cách nào để tra cứu và chèn vào HashMap một cách hiệu quả?


102

Tôi muốn làm như sau:

  • Tra cứu một Veckhóa nhất định và lưu trữ để sử dụng sau.
  • Nếu nó không tồn tại, hãy tạo một khoảng trống Veccho khóa, nhưng vẫn giữ nó trong biến.

Làm thế nào để làm điều này một cách hiệu quả? Đương nhiên tôi nghĩ rằng tôi có thể sử dụng 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
    }
};

Khi tôi thử nó, nó cho tôi các lỗi như:

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

Tôi đã kết thúc với việc làm một cái gì đó như thế này, nhưng tôi không thích thực tế là nó thực hiện tra cứu hai lần ( map.contains_keymap.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ó cách nào an toàn để làm điều này chỉ với một cái match?

Câu trả lời:


119

Các entryAPI được thiết kế cho việc này. Ở dạng thủ công, nó có thể trông giống như

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

Hoặc người ta có thể sử dụng biểu mẫu ngắn gọn:

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

Nếu defaultOK / rẻ để tính toán ngay cả khi nó không được chèn vào, nó cũng có thể là:

map.entry(key).or_insert(default)

Cảm ơn cho một anser nhanh chóng! Bây giờ tôi đã học được rằng tôi nên xem xét sâu hơn một chút về các tài liệu.
Yusuke Shinyama

22
vấn đề với entry () là bạn luôn phải sao chép khóa, có cách nào để tránh điều này không?
Pascalius

@Pascalius bạn có thể làm loại chìa khóa của bạn &T(nếu các phím sống lâu hơn bản đồ, ví dụ như chuỗi tĩnh) hoặc Rc<T>thay T- nhưng nó không đẹp trong cả hai trường hợp
kbolino

@Pascalius: bạn có thể sử dụng v.key()trong các biểu thức default, và sau đó nó sẽ nhận được một tài liệu tham khảo để chìa khóa vì nó tồn tại trong hashmap, vì vậy bạn có thể tránh một bản sao theo cách này
Chris Beck
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.