为什么HashMap :: iter.nth(0)为每次执行运行提供不同的输出?

时间:2018-05-08 10:01:27

标签: hashmap iterator rust

鉴于以下计划:

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?

3 个答案:

答案 0 :(得分:7)

这是因为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")
}

答案 1 :(得分: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

答案 2 :(得分:2)

正如其他人所说,订单是不可预测的。但是,使用不同的哈希可以至少为您提供可重现的结果。例如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中的默认哈希函数是安全的。