обмен двух записей в HashMap - PullRequest
4 голосов
/ 05 февраля 2020

у меня есть простое HashMap; скажем HashMap<char, char>.

есть ли способ поменять два элемента в этой хэш-карте, используя std::mem::swap (или любой другой метод)?

Конечно, есть простой способ получить значения с помощью get и затем заменить их на insert - но это дважды вызовет хэш (один раз для получения, а затем для вставки), и я искал способ обойти второй вызов хэша (скорее из любопытства, чем из-за производительности).

я попробовал вот что (в нескольких версиях, ни одна из которых не работала - и, как отмечено в комментариях: entry не будет делать то, что я ожидаю, даже если я получу это мимо компилятора):

use std::collections::HashMap;
use std::mem::swap;

let mut hash_map: HashMap<char, char> = HashMap::default();
hash_map.insert('A', 'Z');
hash_map.insert('B', 'Y');

swap(&mut hash_map.entry('A'), &mut hash_map.entry('B'));

теперь компилятор жалуется (я понимаю, почему это должно)

error[E0499]: cannot borrow `hash_map` as mutable more than once at a time
   --> tests.rs:103:42
    |
103 |      swap(&mut hash_map.entry('A'), &mut hash_map.entry('B'));
    |      ----      --------                  ^^^^^^^^ second mutable borrow occurs here
    |      |         |
    |      |         first mutable borrow occurs here
    |      first borrow later used by call

также просто получая эти два значения терпят неудачу более или менее одинаково:

let mut a_val = hash_map.get_mut(&'A').expect("failed to get A value");
let mut b_val = hash_map.get_mut(&'B').expect("failed to get B value");
swap(&mut a_val, &mut b_val);

есть ли способ просто поменять две записи в HashMap?

Ответы [ 2 ]

3 голосов
/ 05 февраля 2020

Я не вижу безопасного способа сделать это:

use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.insert('A', 'Z');
    map.insert('B', 'Y');

    let a = map.get_mut(&'A').unwrap() as *mut char;
    let b = map.get_mut(&'B').unwrap() as *mut char;
    unsafe {
        std::ptr::swap(a, b);
    }

    assert_eq!(map.get(&'A'), Some(&'Y'));
    assert_eq!(map.get(&'B'), Some(&'Z'));
}
1 голос
/ 05 февраля 2020

Есть один совершенно безопасный способ, который я могу придумать, чтобы сделать это безопасно, но это супер неэффективно: вам нужно получить значения два & mu, что означает, что заемщик должен знать, что они не перекрываются. Пропустив встроенную функцию в соответствии с split_mut (или коллекцию, обрабатываемую специально), единственный способ, который я вижу, - это итеративно перебрать всю коллекцию, сохранить ссылки на интересующие вас элементы и поменять местами:

    let mut h = HashMap::new();
    h.insert("a", "a");
    h.insert("b", "b");

    let mut it = h.iter_mut();
    let e0 = it.next().unwrap();
    let e1 = it.next().unwrap();
    std::mem::swap(e0.1, e1.1);
    println!("{:?}", h);

Требуется линейный обход карты, пока вы не найдете записи, значения которых вы хотите поменять. Так что, несмотря на то, что у Эдварда есть преимущество в том, что он вообще не хэширует, ответ, вероятно, более практичный.

...