indexmap: a Rust hash table that keeps insertion order and adds integer lookup
A hash table with consistent order and fast iteration; access items by key or sequence index
At a glance
- What is it?
- indexmap is a pure-Rust map and set that preserves insertion order and lets you fetch entries by key or by index. It is a drop-in-shaped alternative to std HashMap, with one removal operation that quietly reorders the map.
- Who is it for?
- Adopt indexmap when you need deterministic iteration order, index-based access, or both, and you are willing to accept that swap_remove changes order while shift_remove costs a shift. Do not adopt it as a blanket replacement for HashMap in hot lookup paths without measuring, because the README itself says lookup is slow-ish when the CPU cache is the limit.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 27 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem indexmap solves that std HashMap does not
Rust's std HashMap gives you no promise about iteration order. If you print a map, serialize it, or walk it to build a report, the sequence can differ between runs and between builds. That is fine for pure lookup, and painful for anything where order is part of the output. indexmap is aimed at that second case: a hash table whose iteration order is independent of the hash function and of the keys' hash values, so the same insertions produce the same walk order. The README describes the crate as a pure-Rust hash table which preserves insertion order in a limited sense, and the limitation is stated plainly: order survives only as long as you do not call .remove(), .swap_remove(), or other methods that explicitly change order. The audience is Rust developers building anything where the map is also a sequence: config that gets serialized, a plugin registry that is listed in load order, a cache whose eviction you want to reason about. The Cargo.toml keywords are hashmap and no_std, and the categories are data-structures and no-std, which tells you the crate is meant to work outside hosted environments too.
How the data structure is laid out and why iteration is fast
The README gives the construction in one line: a raw hash table of key-value indices, and a vector of key-value pairs. That split is the whole design. The hash table stores small integers pointing into the vector, and the vector stores the actual entries densely and in insertion order. Iteration therefore walks a contiguous vector rather than chasing buckets, which is why the README calls iteration very fast. Lookup goes the other way: hash the key, find the index in the table, then dereference into the vector. The README is honest that this second step makes lookup fast-ish rather than uniformly fast, because the key-value pairs live separately from the table, and the cost becomes visible when CPU cache size is the limiting factor. Removal is described as fast because it moves memory only inside the table and uses a single swap in the vector. The crate uses hashbrown for the inner table, the same crate std HashMap uses, so the hashing and probing behaviour is not exotic. If you want the properties of IndexMap, or its strongest performance points fit your workload, the README says it might be the best hash table implementation, and it cites rustc PR45282, where IndexMap was tried as the hashmap in rustc and performance was roughly on par across the whole workload. That is a comparison of one workload, not a general benchmark, and it is the only performance evidence the README offers.
Installing indexmap and doing a first real lookup
The crate is published on crates.io, and the README badge links to it. Add it as a dependency in Cargo.toml. The version in the repository manifest is 2.14.2, and the manifest sets rust-version to 1.85 and edition to 2024, so an older toolchain will refuse to build it.
[dependencies]
indexmap = "2.14.2"With the default features, std is enabled. If you are targeting a no_std environment, the manifest shows a std feature that is on by default, so you would disable default features and opt back into what you need. Serde support is not on by default either; the manifest lists it as a feature named serde that pulls in serde_core and serde.
[dependencies]
indexmap = { version = "2.14.2", default-features = false }Once the dependency resolves, the basic use is to insert pairs and then read them back either by key or by position. The README states that lookup is allowed by either hash table key or numerical index, which is the feature that separates this from an ordinary map. A first program can insert entries, print them in insertion order, and then index into the map positionally. Because the README does not print a full code sample, the exact method names for indexed access are best confirmed against the API documentation at docs.rs, which the manifest points to as the documentation URL. The one method name the README does commit to is shift_remove, described as the alternate removal that preserves relative order, in contrast to remove and swap_remove.
Removal is where the ordering guarantee breaks
This is the sharpest limitation in the project, and the README does not soften it. The crate preserves insertion order as long as you do not call .remove(), .swap_remove(), or other methods that explicitly change order. The default removal path is not order-preserving, and the documentation says so directly. swap_remove is the fast one: it moves the last element into the vacated slot, which keeps the vector dense and costs a single swap, but it also moves an unrelated entry to a new position. If your map is also a sequence that a user reads, that swap is visible. The alternative is shift_remove, which the README says does preserve relative order, and the trade is implied by the name: entries after the removed one shift down, so the operation touches more memory. So the real decision is per call site, not per project. A map used to build a serialized config should use shift_remove, or avoid removal entirely. A map used as an unordered pool where order was never promised can use swap_remove and take the cheaper path. The failure mode is quiet: nothing errors, nothing panics, the map simply iterates in a different sequence than the code that produced it expected. If order is part of your output contract, that is a correctness bug that will not show up in a type check.
indexmap vs std HashMap and the ordermap wrapper
The comparison people reach for is indexmap versus HashMap, and the README answers it by construction rather than by benchmark. HashMap optimizes for the lookup path and gives up order. indexmap keeps a dense vector of entries alongside the table, so it buys deterministic iteration and positional access at the cost of an extra indirection on lookup. If your workload is dominated by get calls on a large map that does not fit in cache, that indirection is the part that hurts, and the README says as much. If your workload iterates more than it looks up, or if order is a requirement, the trade goes the other way. There is also a second option inside the same family: the README notes that the crate was originally released as ordermap, that it was renamed to indexmap to better reflect its features, and that the ordermap crate now exists as a wrapper over indexmap with stronger ordering properties. So if you need ordering guarantees beyond what indexmap offers, the wrapper is the documented path, not a fork. The Cargo.toml also carries a deprecated borsh optional dependency with a comment saying to use borsh's own indexmap feature instead, which is a migration note worth reading before you enable it.
Maintenance, features and licence
The repository is not archived, and the last push was on 2026-09-05, which is recent. The manifest version is 2.14.2, and the README points to RELEASES.md for recent changes rather than listing them inline. The dependency surface is small: equivalent 1.0 and hashbrown 0.17 are the only required dependencies, and both are declared with default-features = false. Everything else is optional, including arbitrary, quickcheck, serde_core, rayon, sval and the deprecated borsh. That matters for upgrade cost, because each optional feature is a separate compatibility surface you can simply not enable. The dev-dependencies pull in itertools, fastrand, quickcheck, fnv and serde with derive, but those do not reach downstream builds. The manifest also sets a release metadata block that restricts releases to the main branch, signs tags, and names tags after the version, which is a detail about how the project ships rather than how you consume it. On licensing, the Cargo.toml declares Apache-2.0 OR MIT, and the repository root contains LICENSE-APACHE and LICENSE-MIT, so both texts are present and you choose. The repository description field on this page says Apache-2.0; the manifest is the more precise statement of the dual offer. That is a description of the files, not legal advice, and the choice between the two licences is yours to make with your own counsel.
Editorial conclusion
Adopt indexmap when you need deterministic iteration order, index-based access, or both, and you are willing to accept that swap_remove changes order while shift_remove costs a shift. Do not adopt it as a blanket replacement for HashMap in hot lookup paths without measuring, because the README itself says lookup is slow-ish when the CPU cache is the limit. Before committing, check that your toolchain meets the rust-version of 1.85 and edition 2024, confirm which optional features you actually need (std is on by default, serde and rayon are not), and decide up front which removal method your code will use, since that choice is the one that silently changes iteration order.
Frequently asked questions
What is indexmap?
It is a pure-Rust hash table that preserves insertion order in a limited sense, and it allows lookup of entries by either hash table key or numerical index. It uses hashbrown for the inner table, the same crate std HashMap uses. The README describes the construction as a raw hash table of key-value indices plus a vector of key-value pairs.
How does indexmap differ from HashMap?
HashMap makes no promise about iteration order, while indexmap keeps order independent of the hash function and adds positional lookup. The cost is an extra indirection on lookup, which the README says becomes visible when CPU cache size is the limiting factor. The README cites rustc PR45282, where IndexMap was tried as the hashmap in rustc and performance was roughly on par across the whole workload.
Does indexmap preserve insertion order after removing an entry?
Not with the default removal path. The README states that order is preserved only as long as you do not call .remove(), .swap_remove(), or other methods that explicitly change order. The alternate .shift_remove() does preserve relative order.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/indexmap-rs-indexmap)