【问题标题】:Why does HashMap::iter.nth(0) give different output for each execution run? [duplicate]为什么 HashMap::iter.nth(0) 为每次执行运行提供不同的输出? [复制]
【发布时间】:2018-10-18 05:57:12
【问题描述】:

鉴于以下程序:

use std::collections::HashMap;

fn main() {
    let mut hm = HashMap::new();
    hm.insert(0, 1);
    hm.insert(1, 1);
    let mut iter = hm.iter();
    println!("{:?}", iter.nth(0).expect("Fatal.").0)
}

每次执行代码都会得到不同的输出:

procyclinsur@procyclinsur:~/Documents/Rust/t1$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/t1`
1
procyclinsur@procyclinsur:~/Documents/Rust/t1$ vim src/main.rs 
procyclinsur@procyclinsur:~/Documents/Rust/t1$ cargo run
   Compiling t1 v0.1.0 (file:///home/procyclinsur/Documents/Rust/t1)
    Finished dev [unoptimized + debuginfo] target(s) in 1.12 secs
     Running `target/debug/t1`
1
procyclinsur@procyclinsur:~/Documents/Rust/t1$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/t1`
1
procyclinsur@procyclinsur:~/Documents/Rust/t1$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/t1`
0
procyclinsur@procyclinsur:~/Documents/Rust/t1$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/t1`
1

我希望程序的每次运行都看到相同的输出。有谁知道这段代码以这种方式运行的原因?如何让它按预期只输出 0?

【问题讨论】:

  • @Stargateur 已修复!
  • @Shepmaster 没错,该帖子确实回答了我的问题,但它并没有以明显的方式这样做。它的标题并不表示我所​​指的问题,示例代码或解释也不是。这篇文章对实际问题有更好的描述。除了将其标记为重复之外,应该有更好的方法来解决这个问题吗?
  • 这实际上是复制的完美用例。它们充当路标,将搜索不同术语的人们重定向到一组规范的答案。将您的问题标记为重复问题绝对没有不好。
  • 否决票是输出为图像,您已修复,因此我已将其删除;谢谢。

标签: hashmap iterator rust


【解决方案1】:

这是因为 HashMap 是无序的。它的iter method

任意顺序访问所有键值对

documentation on collections 描述了这种行为:

对于像HashMap这样的无序集合,项目将在 内部表示最方便的顺序。

为了始终检索特定值,您需要search by the key(或使用不依赖于顺序的Iterator methods之一,例如find);例如:

use std::collections::HashMap;

fn main() {
    let mut hm = HashMap::new();
    hm.insert(0, "a");
    hm.insert(1, "b");
    println!("{:?}", hm.get(&0)) // always Some("a")
}

【讨论】:

  • 谢谢!这正是我想要的!
【解决方案2】:

值得一提的是BTreeMap和对应的iter函数

获取映射条目的迭代器,按键排序。

这可能是一个简单的直接替换。

use std::collections::BTreeMap;

fn main() {
    let mut hm = BTreeMap::new();
    hm.insert(0, 1);
    hm.insert(1, 1);
    let mut iter = hm.iter();
    println!("{:?}", iter.nth(0).expect("Fatal.").0);
}

playground

【讨论】:

  • 感谢 BTreeMap 的提及,这看起来很有趣,我会去看看!
【解决方案3】:

正如其他人所说,顺序是不可预测的。但是,使用不同的哈希器至少可以为您提供可重现的结果。例如FNV hash function,您可以这样使用:

extern crate fnv;

use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use fnv::FnvHasher;

type HashMapFnv<K, V> = HashMap<K, V, BuildHasherDefault<FnvHasher>>;

fn main() {
    let mut hm = HashMapFnv::default();
    hm.insert(0, 1);
    hm.insert(1, 1);
    let mut iter = hm.iter();
    println!("{:?}", iter.nth(0).expect("Fatal.").0)
}

这应该每次都为您提供相同的结果。但是,无法保证该顺序是什么,因此如果您更新 FNV 或使用不同版本的 Rust 本身,您可能会在不同的操作系统上得到不同的结果。

请注意,您不应在任何处理外部数据的应用程序中使用此哈希函数,因为它容易受到哈希冲突攻击。 Rust 中的默认哈希函数是安全的。

【讨论】:

    猜你喜欢
    • 2021-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-05
    • 2014-12-06
    • 2014-12-06
    • 2018-05-02
    • 1970-01-01
    相关资源
    最近更新 更多