Table of Contents

    OrderedDict

    PYTHON & DATA STRUCTURES

    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.

    In one line: 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
    Key difference This order-sensitive comparison is one of the main reasons to still choose 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_cache for ready-made caching
    • Document why you chose OrderedDict so future readers understand the intent

    Common Mistakes

    Bad Using OrderedDict solely to preserve order in modern Python — a regular dict already does this since 3.7.
    Good Reserve OrderedDict for reordering, order-equality, or FIFO popping