在Rust中检查两个HashMap是否具有相同的键集

3
假设我有两个HashMaps(或任何具有将键映射到其他内容的映射结构),map1和map2,并且我想确保它们具有相同的键集。请注意,键在地图中是相同类型的,但值不相同。
我的初始尝试只是:
map1.keys().eq(map2.keys())

虽然第一次尝试这个方法成功了,但是迭代器的eq函数(可以理解为相等判断)似乎比较的是迭代器产生的键值的顺序,而不是检查第二个迭代器中是否存在一个键。这很容易让人误解。另外,HashMap::keys() 产生的迭代器中 键的顺序不是确定的,这使得即使键集合在集合论意义上相同,相等性函数也经常会失败。

因此,我的下一步尝试是创建一个函数来处理这个问题:

fn keys_match<T:std::cmp::Eq + std::hash::Hash,U,V>(map1:&HashMap<T,U>, map2:&HashMap<T,V>) -> bool {
  // Make sure that map1.keys() ⊆ map2.keys()
  for key in map1.keys() {
    match map2.get(key) {
      None => return false,
      Some(_) => {}
    }
  }
  // If map1.keys() ⊆ map2.keys() and their sizes equal, then the sets are equal
  map1.len() == map2.len()
}

注意Rust初学者:我的第一次尝试实际上是在知道映射中的键是String类型的情况下进行的,因此我的函数签名为:
fn keys_match<T,U>(map1:&HashMap<String,T>, map2:&HashMap<String,U>) -> bool

我意识到可以通过要求它们具有Eq和Hash trait来泛型化通用键类型

问题:在Rust中有更简洁的方法吗?


4
这不是一个答案,但你可能首先应该测试大小,这样如果长度不同,就不会浪费时间比较键。 - Jmb
好建议。我想我有点陷入了试图限制早期返回和代码大小的困境中,但你是对的,只要 HashMap::len() 不会迭代键(并且根据您的用例是否倾向于测试相同大小但不同的集合),首先测试长度是一个重要的考虑因素。 - Jemenake
1个回答

14
有没有更简洁的方式在Rust中完成这个任务?
fn keys_match<T: Eq + Hash, U, V>(
    map1: &HashMap<T, U>, 
    map2: &HashMap<T, V>,
) -> bool {
    map1.len() == map2.len() && map1.keys().all(|k| map2.contains_key(k))
}

(Playground)
这段代码相比你的代码有三个改进:
  • 使用Iterator::all可以缩短代码长度。
  • 使用HashMap::contains_key比检查HashMap::get的结果更好。
  • 首先检查长度,因为这是一个便宜的测试,应该首先进行。

我以前从未遇到过迭代器的.all()函数(也没有.any())。现在我会知道在其他支持迭代器的语言中要寻找它们。 - Jemenake
@Jemenake 是的,Iterator 的许多方法非常好用,可以真正缩短您的代码。在我看来,这样做可以使代码更易读 :) - Lukas Kalbertodt
1
map1.keys().any(|k| !map2.contains_key(k)) 可能更高效(最坏情况下的性能相同为 O(n),但最佳情况要好得多)。 - Teymour
2
@TeymourAldridge 没有区别。如果all中的闭包返回false,它也不会再尝试任何进一步的操作并立即停止。allany实际上只是彼此反转的版本。 - Lukas Kalbertodt
@TeymourAldridge 没问题! - Lukas Kalbertodt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接