OrderedDict
Python OrderedDict — The Complete Guide
Master collections.OrderedDict — an order-aware dictionary with reordering, order-sensitive equality, and precise LRU-cache control.
Introduction
OrderedDict is a dictionary subclass from Python's built-in collections module that remembers the
order in which keys were inserted and provides special methods to manipulate that order. Since Python 3.7, regular
dicts also preserve insertion order — so OrderedDict is no longer needed just for ordering. However, it still shines when
you need order-sensitive equality, efficient reordering (move_to_end), or
LIFO/FIFO popping — making it the natural choice for building caches like an LRU.
OrderedDict is a dictionary that treats key order as meaningful — and lets you rearrange it on demand.
Real-World Analogy
The Numbered Waiting Line
Picture a queue where every person's position matters, and the manager can move someone to the front or back on request. A plain
dict remembers who arrived first; an OrderedDict lets you actively reshuffle the line — sending a person to the end or
calling the next in order — which is exactly how an LRU cache works.
Prerequisites
Before You Start
- Python 3.x installed
- Understanding of dictionaries and key/value access
- The class is built in — just
from collections import OrderedDict - Awareness that regular dicts keep insertion order since Python 3.7
- Basic understanding of caching concepts (helpful for LRU)
Getting Started
Create an OrderedDict just like a dict — it preserves the insertion order of keys.
from collections import OrderedDict
od = OrderedDict()
od["a"] = 1
od["b"] = 2
od["c"] = 3
print(od) # OrderedDict([('a', 1), ('b', 2), ('c', 3)])
for key, value in od.items():
print(key, value)
# a 1
# b 2
# c 3
Special Methods
| Method | Purpose |
|---|---|
move_to_end(key, last=True) |
Move an existing key to the end (or start if last=False) |
popitem(last=True) |
Remove and return the last (LIFO) or first (FIFO) item |
keys() / values() / items() |
Order-preserving views (like dict) |
__eq__ |
Order-sensitive equality between two OrderedDicts |
Reordering with move_to_end
Move any existing key to the end or the beginning without deleting it.
from collections import OrderedDict
od = OrderedDict([("a", 1), ("b", 2), ("c", 3)])
od.move_to_end("a") # move "a" to the end
print(od) # OrderedDict([('b', 2), ('c', 3), ('a', 1)])
od.move_to_end("c", last=False) # move "c" to the front
print(od) # OrderedDict([('c', 3), ('b', 2), ('a', 1)])
Controlled Popping with popitem
popitem lets you choose which end to remove from — LIFO (default) or FIFO.
from collections import OrderedDict
od = OrderedDict([("a", 1), ("b", 2), ("c", 3)])
print(od.popitem()) # ('c', 3) — last (LIFO)
print(od.popitem(last=False)) # ('a', 1) — first (FIFO)
print(od) # OrderedDict([('b', 2)])
Order-Sensitive Equality
Two OrderedDicts are equal only if their order matches too — unlike regular dicts.
from collections import OrderedDict
od1 = OrderedDict([("a", 1), ("b", 2)])
od2 = OrderedDict([("b", 2), ("a", 1)])
print(od1 == od2) # False — different order!
# Regular dicts ignore order:
print({"a": 1, "b": 2} == {"b": 2, "a": 1}) # True
OrderedDict today.
Real Example: A Simple LRU Cache
The classic use — move_to_end plus popitem makes a Least-Recently-Used cache trivial.
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity):
self.cache = OrderedDict()
self.capacity = capacity
def get(self, key):
if key not in self.cache:
return -1
self.cache.move_to_end(key) # mark as recently used
return self.cache[key]
def put(self, key, value):
if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = value
if len(self.cache) > self.capacity:
self.cache.popitem(last=False) # evict least recently used
cache = LRUCache(2)
cache.put(1, "a")
cache.put(2, "b")
cache.get(1) # touch key 1
cache.put(3, "c") # evicts key 2 (least recently used)
print(cache.get(2)) # -1 (evicted)
print(cache.get(1)) # a
OrderedDict vs Regular dict
| Feature | OrderedDict | dict (3.7+) |
|---|---|---|
| Preserves insertion order | Yes | Yes |
| Order-sensitive equality | Yes | No (order ignored) |
move_to_end() |
Yes | No |
popitem(last=False) |
Yes (FIFO option) | LIFO only |
| Memory overhead | Slightly higher | Lower |
Performance
OrderedDict keeps a doubly-linked list of keys alongside the hash table, so its core operations remain average
\(O(1)\), including the special reordering method:
\[ T_{\text{get}} = T_{\text{set}} = T_{\text{move\_to\_end}} = O(1) \]
This constant-time reordering is exactly what makes an OrderedDict-based LRU cache efficient.
When Should You Still Use It?
Use OrderedDict
- You need order-sensitive equality
- You need
move_to_end()reordering - You need FIFO popping via
popitem(last=False) - Building an LRU or similar cache
Just Use dict
- You only need to preserve insertion order
- Order equality doesn't matter
- You want the lowest memory footprint
- Simple key/value storage
Best Practices
Do This
- Use OrderedDict when order equality genuinely matters
- Use
move_to_end()for LRU-style "recently used" tracking - Use
popitem(last=False)for FIFO eviction - Prefer a plain dict when you only need insertion order (3.7+)
- Consider
functools.lru_cachefor ready-made caching - Document why you chose OrderedDict so future readers understand the intent