I've observed that HashMap
has a different order of elements even with the same data on the next program start. It looks like HashMap
uses some absolute addresses to sort elements. Is there any other HashMap
implementation, which has the same behaviour if the same data was inserted?

- 388,571
- 95
- 1,107
- 1,366

- 8,670
- 28
- 113
- 206
4 Answers
I've observed that
HashMap
has a different order of elements even with the same data on the next program start.
You don't have to observe anything, this is documented by HashMap
:
By default,
HashMap
uses a hashing algorithm selected to provide resistance against HashDoS attacks. The algorithm is randomly seeded, and a reasonable best-effort is made to generate this seed from a high quality, secure source of randomness provided by the host without blocking the program.
It's worth noting that this means that two HashMap
s with the same set of inserted values in the same program run will likely have different ordering:
use std::collections::HashMap;
fn main() {
let a = (0..100).zip(100..200);
let hash_one: HashMap<_, _> = a.clone().collect();
let hash_two: HashMap<_, _> = a.clone().collect();
// prints "false", most of the time
println!("{}", hash_one.into_iter().eq(hash_two));
}
The documentation also tells you how to address the problem:
The hashing algorithm can be replaced on a per-
HashMap
basis using thedefault
,with_hasher
, andwith_capacity_and_hasher
methods. Many alternative algorithms are available on crates.io, such as the fnv crate.
Since I worked on twox-hash, I'll show that as an example:
use std::hash::BuildHasherDefault;
use std::collections::HashMap;
use twox_hash::XxHash;
let mut hash: HashMap<_, _, BuildHasherDefault<XxHash>> = Default::default();
hash.insert(42, "the answer");
assert_eq!(hash.get(&42), Some(&"the answer"));
That being said, relying on the order of a HashMap
sounds like a bad idea. Perhaps you should use a different data structure, such as a BTreeMap
.
In other cases, you actually care about the order of insertion. For that, the indexmap crate is appropriate.

- 11,684
- 3
- 52
- 85

- 388,571
- 95
- 1,107
- 1,366
-
3Actually, the order of iteration in `indexmap` is not *strictly* the order of insertion. Why? Because when removing an element, the last inserted element is swapped with the element to be removed. It's still fully deterministic for a given sequence of insertions/removals. – Matthieu M. Jan 25 '19 at 15:44
-
Newer versions of `indexmap` apparently have a `shift_remove` function, which will preserve the insertion-order even after removals. See [here](https://github.com/bluss/indexmap/issues/220), [here](https://github.com/bluss/indexmap/issues/90), and [here](https://github.com/bluss/indexmap/pull/99) for some more info. – Venryx Mar 17 '22 at 23:39
I believe linked-hash-map is the de facto crate for this.

- 35,686
- 13
- 80
- 98
-
The original post did not mention maintaining insertion order as necessary, but maintaining *consistent* order. If that's the requirement, I think using an alternate hash function as suggested by @Shepmaster is preferable as you're likely to get better performance. – Michael Mior Aug 04 '22 at 19:33
Use HashMap::with_hasher()
with something other than the default RandomState
.

- 388,571
- 95
- 1,107
- 1,366

- 13,636
- 2
- 46
- 67
-
8
-
@StepanYakovenko The [readme for HashMap](https://doc.rust-lang.org/std/collections/struct.HashMap.html) says you can find alternative hashers with [this search](https://crates.io/keywords/hasher). – Venryx Mar 31 '22 at 15:21
The built-in BTreeMap is a great option for this.
I was having a similar problem where the assert_eq! diff was in a random order each time, making debugging difficult for HashMap, but BTreeMap has sorted keys, so it doesn't have this problem.
(source: https://users.rust-lang.org/t/sort-hashmap-data-by-keys/37095/2)

- 1,940
- 1
- 20
- 24