1027-btreemap-sorted — BTreeMap: Sorted Key Iteration
Tutorial
The Problem
Hash maps provide O(1) average lookup but iterate in arbitrary order, making them unsuitable for tasks that require processing keys in sorted order — leaderboards, time-series data, range queries, and sorted output. B-trees, invented by Bayer and McCreight in 1972 for database indexes, maintain sorted order while keeping operations O(log n). Rust's BTreeMap is a cache-friendly B-tree optimized for modern hardware.
This is the data structure behind database indexes, std::map in C++, and OCaml's Map.Make module.
🎯 Learning Outcomes
BTreeMap over HashMapBTreeMap in guaranteed sorted key orderrange method for efficient range queries without scanning all entriesfirst_key_value and last_key_value for O(log n) min/max accessCode Example
//! 1027: BTreeMap — Sorted Key Iteration
//!
//! Rust's `BTreeMap` keeps keys in sorted order (B-tree internally),
//! mirroring OCaml's tree-based `Map` module.
use std::collections::BTreeMap;
/// Build a `BTreeMap` and iterate — keys come out sorted.
pub fn sorted_iteration() {
let m: BTreeMap<i32, &str> = [
(5, "five"),
(1, "one"),
(3, "three"),
(7, "seven"),
(2, "two"),
]
.into_iter()
.collect();
let keys: Vec<i32> = m.keys().copied().collect();
assert_eq!(keys, [1, 2, 3, 5, 7]);
let values: Vec<&str> = m.values().copied().collect();
assert_eq!(values, ["one", "two", "three", "five", "seven"]);
}
/// Range queries using the `range` method.
pub fn range_query() {
let m: BTreeMap<i32, &str> = [(1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e")]
.into_iter()
.collect();
let range_keys: Vec<i32> = m.range(2..=4).map(|(&k, _)| k).collect();
assert_eq!(range_keys, [2, 3, 4]);
let tail: Vec<i32> = m.range(3..).map(|(&k, _)| k).collect();
assert_eq!(tail, [3, 4, 5]);
}
/// First and last key (min/max) via `first_key_value` / `last_key_value`.
pub fn min_max() {
let m: BTreeMap<i32, &str> = [(10, "ten"), (3, "three"), (7, "seven")]
.into_iter()
.collect();
let (&min_k, _) = m.first_key_value().unwrap();
let (&max_k, _) = m.last_key_value().unwrap();
assert_eq!(min_k, 3);
assert_eq!(max_k, 10);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sorted_iteration() {
sorted_iteration();
}
#[test]
fn test_range_query() {
range_query();
}
#[test]
fn test_min_max() {
min_max();
}
#[test]
fn test_from_iterator() {
let pairs = vec![(3, "c"), (1, "a"), (2, "b")];
let m: BTreeMap<_, _> = pairs.into_iter().collect();
let keys: Vec<_> = m.keys().copied().collect();
assert_eq!(keys, [1, 2, 3]);
}
}Key Differences
Map.Make is always sorted by key — there is no unsorted alternative; Rust has both HashMap (unsorted) and BTreeMap (sorted)..range(lo..=hi) method; OCaml uses to_seq_from plus take_while for the equivalent.Map is persistent (update returns a new version sharing structure); Rust's BTreeMap is mutable.OCaml Approach
OCaml's Map.Make(Ord) is always a sorted tree map. It has no hash map variant in the standard library, so range queries are natural:
module IntMap = Map.Make(Int)
let range_query m lo hi =
IntMap.to_seq_from lo m
|> Seq.take_while (fun (k, _) -> k <= hi)
|> List.of_seq
Map.to_seq_from starts iteration at a given key, enabling efficient range scans without the explicit range API.
Full Source
//! 1027: BTreeMap — Sorted Key Iteration
//!
//! Rust's `BTreeMap` keeps keys in sorted order (B-tree internally),
//! mirroring OCaml's tree-based `Map` module.
use std::collections::BTreeMap;
/// Build a `BTreeMap` and iterate — keys come out sorted.
pub fn sorted_iteration() {
let m: BTreeMap<i32, &str> = [
(5, "five"),
(1, "one"),
(3, "three"),
(7, "seven"),
(2, "two"),
]
.into_iter()
.collect();
let keys: Vec<i32> = m.keys().copied().collect();
assert_eq!(keys, [1, 2, 3, 5, 7]);
let values: Vec<&str> = m.values().copied().collect();
assert_eq!(values, ["one", "two", "three", "five", "seven"]);
}
/// Range queries using the `range` method.
pub fn range_query() {
let m: BTreeMap<i32, &str> = [(1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e")]
.into_iter()
.collect();
let range_keys: Vec<i32> = m.range(2..=4).map(|(&k, _)| k).collect();
assert_eq!(range_keys, [2, 3, 4]);
let tail: Vec<i32> = m.range(3..).map(|(&k, _)| k).collect();
assert_eq!(tail, [3, 4, 5]);
}
/// First and last key (min/max) via `first_key_value` / `last_key_value`.
pub fn min_max() {
let m: BTreeMap<i32, &str> = [(10, "ten"), (3, "three"), (7, "seven")]
.into_iter()
.collect();
let (&min_k, _) = m.first_key_value().unwrap();
let (&max_k, _) = m.last_key_value().unwrap();
assert_eq!(min_k, 3);
assert_eq!(max_k, 10);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sorted_iteration() {
sorted_iteration();
}
#[test]
fn test_range_query() {
range_query();
}
#[test]
fn test_min_max() {
min_max();
}
#[test]
fn test_from_iterator() {
let pairs = vec![(3, "c"), (1, "a"), (2, "b")];
let m: BTreeMap<_, _> = pairs.into_iter().collect();
let keys: Vec<_> = m.keys().copied().collect();
assert_eq!(keys, [1, 2, 3]);
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sorted_iteration() {
sorted_iteration();
}
#[test]
fn test_range_query() {
range_query();
}
#[test]
fn test_min_max() {
min_max();
}
#[test]
fn test_from_iterator() {
let pairs = vec![(3, "c"), (1, "a"), (2, "b")];
let m: BTreeMap<_, _> = pairs.into_iter().collect();
let keys: Vec<_> = m.keys().copied().collect();
assert_eq!(keys, [1, 2, 3]);
}
}
Deep Comparison
BTreeMap: Sorted Key Iteration — Comparison
Core Insight
Both languages provide tree-based sorted maps. OCaml's Map uses a balanced binary tree (AVL); Rust's BTreeMap uses a B-tree optimized for cache locality. Both guarantee sorted iteration.
OCaml Approach
Map.Make(Ord) creates a map module for a given key typeadd returns a new mapsplit for range queries: splits map into (below, at, above)min_binding / max_binding for extremesfold for ordered traversalRust Approach
BTreeMap<K: Ord, V> — no functor neededinsert, or built from iterators via collect()range() accepts RangeBounds for efficient sub-range iterationiter().next() / iter().next_back() for min/maxComparison Table
| Feature | OCaml (Map) | Rust (BTreeMap) |
|---|---|---|
| Structure | AVL tree | B-tree |
| Mutability | Immutable | Mutable |
| Key constraint | Ord module (functor) | Ord trait (generic) |
| Sorted iteration | fold / iter | iter() / keys() / values() |
| Range query | split (returns 3 parts) | range(a..=b) (lazy iterator) |
| Min/Max | min_binding / max_binding | first_key_value() / last_key_value() |
| Cache locality | Poor (pointer-heavy) | Good (B-tree nodes) |
Exercises
BTreeMap<u64, f64> (timestamp -> value) and write a window_average(start: u64, end: u64) function using range.ranked_top_k(map: &BTreeMap<String, usize>, k: usize) -> Vec<(&str, usize)> that returns the k most frequent items in alphabetical order.BTreeMap at a given key into two maps using split_off.