【问题标题】:Why is BTreeMap hashable, and not HashMap?为什么 BTreeMap 是可散列的,而不是 HashMap?
【发布时间】:2023-01-14 00:29:48
【问题描述】:

来自这里的 Python。

我想知道为什么 BTreeMap 是可散列的。我并不惊讶 Hashmap 不是,但我不明白为什么 BTreeMap 是。

例如,我可以这样做:

let mut seen_comb: HashSet<BTreeMap<u8, u8>> = HashSet::new();
seen_comb.insert(BTreeMap::new());

但我不能这样做:

let mut seen: HashSet<HashMap<u8, u8>> = HashSet::new();
seen.insert(HashMap::new());

因为我得到:

error[E0599]: the method `insert` exists for struct `HashSet<HashMap<u8, u8>>`, but its trait bounds were not satisfied
   --> src/main.rs:14:10
    |
14  |     seen.insert(HashMap::new());
    |          ^^^^^^ method cannot be called on `HashSet<HashMap<u8, u8>>` due to unsatisfied trait bounds
    |
   ::: /home/djipey/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/collections/hash/map.rs:209:1
    |
209 | pub struct HashMap<K, V, S = RandomState> {
    | ----------------------------------------- doesn't satisfy `HashMap<u8, u8>: Hash`
    |
    = note: the following trait bounds were not satisfied:
            `HashMap<u8, u8>: Hash`

在 Python 中,我不能将 dict 放入集合中,因此 BTreeMap 的行为令我感到惊讶。

有人可以在这里提供解释吗?

【问题讨论】:

  • 这只是一个猜测,但元素散列的顺序会影响结果,HashMap 没有确定的顺序。即使两个HashMap具有相同的元素,顺序也可以不同。

标签: rust hashmap


【解决方案1】:

原因是BTreeMap 有一个确定的迭代顺序而HashMap 没有。引用 Hash 特征中的文档,

在同时实现 Hash 和 Eq 时,保持以下属性很重要:

k1 == k2 -> hash(k1) == hash(k2)

换句话说,如果两个键相等,则它们的哈希值也必须相等。 HashMap 和 HashSet 都依赖于这种行为。

由于 HashMap 的迭代顺序是不确定的,因此无法保证此行为,因此只要输入不同的 HashMap,数据就会以不同的顺序馈送到 Hasher,即使它们包含相同的内容元素,破坏了Hash的约定,并在HashSetHashMap中使用时导致坏事发生。

但是,BTreeMap 的全部要点在于它是一个有序映射,这意味着它的迭代按排序顺序进行,这是完全确定的,这意味着可以满足 Hash 的约定,因此提供了一个实现。

请注意,这两种行为都不同于 Python 的 Dict,它按插入顺序迭代事物。

【讨论】:

  • 好的,有道理,谢谢。我差点问为什么 Python 字典不是可散列的(python 中的可散列对象必须满足相同的条件),但您对插入顺序的评论清除了这一点。在 Python 中,因为字典是按插入顺序排序的,所以我可以有 2 个内容相同但迭代顺序不同的字典
【解决方案2】:

其实HashMap实现Hash的答案就在那里: how-to-implement-a-hash-function-for-a-hashset-hashmap

这是代码示例(playground)

use std::collections::{ HashSet, HashMap, hash_map::DefaultHasher, };
use core::hash::{ Hasher, Hash, };

#[derive(Debug, PartialEq,Eq)]
struct MyMap<K,V>(HashMap<K,V>) where K: Eq + Hash;

impl<K,V> Hash for MyMap<K,V> where K: Eq + Hash, V: Hash, {
    fn hash<H>(&self, h: &mut H) where H: Hasher { 
        let hasher = DefaultHasher::new();
        let commut_mix = self.0.iter().map(|(k,v)| {
            let mut in_h = hasher.clone();
            k.hash(&mut in_h);
            v.hash(&mut in_h);
            in_h.finish() as u128
        }).sum();
        h.write_u128(commut_mix);
    }
}

fn main() {
    let map1 = MyMap([(0,"zero"), (2,"deux"),].into_iter().collect());
    let map2 = MyMap([(1,"un"), (3,"trois"), ].into_iter().collect());
    let map2bis = MyMap([(1,"un"), (3,"trois"), ].into_iter().collect());
    let map3 = MyMap([(0,"zero"), (1,"un"), (2,"deux"), ].into_iter().collect());
    let map4 = MyMap([(0,"zero"), (2,"deux"), (3,"trois"), ].into_iter().collect());
    let set: HashSet<_> = [map1, map2, map3, map4, map2bis].into_iter().collect();
    println!("set -> {:?}", set);
}

结果:

Standard Error

   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 5.32s
     Running `target/debug/playground`

Standard Output

set -> {MyMap({1: "un", 0: "zero", 2: "deux"}), MyMap({0: "zero", 2: "deux"}), MyMap({3: "trois", 0: "zero", 2: "deux"}), MyMap({1: "un", 3: "trois"})}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-08-07
    • 2020-03-30
    • 1970-01-01
    • 1970-01-01
    • 2021-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多