【问题标题】:swapping two entries of a HashMap交换 HashMap 的两个条目
【发布时间】:2020-05-21 07:11:07
【问题描述】:

我有一个简单的HashMap;说HashMap<char, char>

有没有办法使用std::mem::swap(或任何其他方法)交换此哈希图中的两个元素?

当然,有一种简单的方法是用get 获取值,然后用insert 替换它们——但这会触发哈希器两次(一次用于获取,然后用于插入),我正在寻找一种方法- 执行第二次哈希调用(更多的是出于好奇而不是性能)。

我尝试的是这个(在几个版本中;没有一个有效 - 正如 cmets 中所说: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 的两个条目?

【问题讨论】:

  • entry() 方法创建 Entry{} 的实例,其中包含键和值的引用。在封闭范围内对这两个实例进行内存交换可能不会产生任何效果。
  • @ÖmerErden 哦,你是对的。但get 根据文档返回引用。知道如何交换这些元素吗?
  • 你能说一下为什么要交换一个 hashmap 的两个值吗?算法是什么?我们很好奇。
  • @Stargateur 作为一个练习,我将之前用 python 制作的演示转换为 rust。该演示展示了如何使用模拟退火攻击替代密码。密码的关键是哈希图(替换表),我需要能够交换元素。

标签: rust hashmap swap


【解决方案1】:

我看不出任何安全的方法:

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

【讨论】:

  • 哈哈,刚刚得到了相同的代码(与slice::swap的源代码相比)。谢谢!
  • 如果这是安全的,我不明白为什么它不存在于标准中
  • @Stargateur 我不认为这是经常使用的......(但我可能错了)。
【解决方案2】:

我可以想到一种完全安全的方法来安全地执行此操作,但效率极低:您想要的是获得两个 &mut 值,这意味着 Borackck 需要知道它们不重叠。缺少类似于 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);

它需要对映射进行线性遍历,直到找到要交换其值的条目。因此,即使这具有根本不散列的优点,edwardw 的答案可能更实用。

【讨论】:

  • 感谢您的回答!但是,是的,线性横向是您在使用哈希图时要避免的。
  • 请注意,没有办法完全用安全代码实现可变迭代器,原因相同:借用检查器无法确定可变引用 @ 987654323@ 分发的方法都是不重叠的。所以这个“解决方案”利用可变迭代器实现中的不安全代码来获得两个具有安全代码的可变引用。
  • @SvenMarnach 在技术上是正确的,但是在查看代码库的“不安全性”时,AFAIK 永远不会考虑安全 stdlib API 中的不安全代码,否则就不会有“安全生锈”代码库因为几乎所有东西最终都以系统调用(不安全)或原始指针(也不安全)结束。
  • 我并不是有意反驳你所说的任何内容。我只是想添加一些上下文。你完全正确,使用安全的 API 就是安全的代码。
  • 我只使用了“解决方案”的吓人引号,因为我认为这个答案实际上并不能解决任何问题,正如您自己所指出的那样。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-11
  • 1970-01-01
  • 1970-01-01
  • 2011-01-06
  • 2011-05-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多