An OrderedDict is a dictionary subclass from the collections module that remembers insertion order and adds methods to reorder its keys; since Python 3.7, a regular dict also keeps insertion order, so OrderedDict is only needed for its extra features today. Those features are move_to_end(), popitem(last=False) and order-sensitive equality, which make it the classic base for an LRU cache.
from collections import OrderedDict
user = OrderedDict(name="lokesh", id=100, email="admin@gmail.com")
user.move_to_end("name") # keys: id, email, name
first = user.popitem(last=False) # ('id', 100)
We compare OrderedDict with dict, create and update one, reorder keys, check equality, and build a small LRU cache. The examples run on Python 3.14.
1. OrderedDict vs dict
Older articles say that a regular dictionary returns its keys in arbitrary order. That was true before Python 3.6; since Python 3.7, insertion order is part of the language guarantee. The differences that remain are in the extra methods and in equality.
| Feature | dict | OrderedDict |
|---|---|---|
| Keeps insertion order | Yes (3.7+) | Yes |
| move_to_end(key, last=True) | No | Yes |
| popitem(last=False), remove the first item | No (popitem() removes the last) | Yes |
| Equality checks order | No | Yes, between two OrderedDicts |
| Memory | Smaller | Larger |
2. Creating and Updating an OrderedDict
An OrderedDict is created like a dict, from keyword arguments, a list of pairs or fromkeys(). Updating an existing key keeps its position, and a new key goes to the end.
user = OrderedDict()
user["name"] = "lokesh"
user["id"] = 100
user["email"] = "admin@gmail.com"
user["email"] = "admin@howtodoinjava.com" # position unchanged
user["location"] = "India" # added at the end
keys = list(user) # ['name', 'id', 'email', 'location']
defaults = OrderedDict.fromkeys(["id", "name"], None) # OrderedDict({'id': None, 'name': None})
Deleting a key with del and adding it again places it at the end, because it counts as a new insertion.
3. Reordering with move_to_end() and popitem()
The move_to_end() method moves an existing key to the end, or to the front with last=False, in constant time. The popitem() method removes the last item by default and the first item with last=False.
od = OrderedDict.fromkeys("abcde")
od.move_to_end("b") # a c d e b
od.move_to_end("e", last=False) # e a c d b
last = od.popitem() # ('b', None)
first = od.popitem(last=False) # ('e', None)
missing = od.move_to_end("z") # KeyError: 'z'
A regular dict can only remove its last item with popitem(); moving a key to the front needs rebuilding the whole dictionary.
4. Equality Checks Order
Two OrderedDict objects are equal only when they have the same items in the same order. Comparing an OrderedDict with a regular dict ignores the order, like comparing two dicts.
a = OrderedDict([("x", 1), ("y", 2)])
b = OrderedDict([("y", 2), ("x", 1)])
same_od = a == b # False, order differs
same_dict = dict(a) == dict(b) # True
mixed = a == {"y": 2, "x": 1} # True, a plain dict ignores order
5. Building an LRU Cache
A least recently used (LRU) cache keeps a fixed number of entries and evicts the one that has not been used for the longest time. With move_to_end() on each access and popitem(last=False) on overflow, the oldest entry is always at the front.
class LRUCache:
def __init__(self, capacity):
self.capacity = capacity
self.data = OrderedDict()
def get(self, key):
if key not in self.data:
return None
self.data.move_to_end(key)
return self.data[key]
def put(self, key, value):
self.data[key] = value
self.data.move_to_end(key)
if len(self.data) > self.capacity:
self.data.popitem(last=False)
cache = LRUCache(2)
cache.put("a", 1)
cache.put("b", 2)
cache.get("a") # touches a, so b is now the oldest
cache.put("c", 3) # evicts b
keys = list(cache.data) # ['a', 'c']
For caching function results, functools.lru_cache does the same with a decorator and needs no class.
6. OrderedDict FAQs
Code written for Python 3.6 and older often still imports OrderedDict, which raises the question of when to keep it.
6.1. Should new code use OrderedDict or dict?
A plain dict is the default, because it keeps insertion order and uses less memory. OrderedDict is the right choice when the code reorders keys or needs order-sensitive equality.
6.2. Can we iterate an OrderedDict in reverse?
Yes, reversed(od) yields the keys from last to first. A regular dict supports reversed() as well since Python 3.8.
6.3. Does json keep the order of an OrderedDict?
Yes. json.dumps() writes keys in iteration order for both dict and OrderedDict, and json.loads(text, object_pairs_hook=OrderedDict) reads them back into an OrderedDict.
7. Conclusion
Regular dictionaries keep insertion order since Python 3.7, so new code starts with dict and reaches for OrderedDict when keys must be reordered. A small LRU cache is the typical case, and functools.lru_cache covers caching function results without any class.
8. References
Happy Learning !!