Time to wire the list into the cache itself. You swap the value map for a map from key to node, insert new entries at the front, and evict from the tail - so every Get, Put, and eviction is O(1). The map still stores insertion order, so behaviour matches the FIFO cache exactly.
Rebuild Put and Get on the node list, inserting at the front and evicting the tail.
Everything so far has been preparation; now the list becomes the cache. The value
map turns into a map from key to *node, so a Get is a map lookup that hands
back the node’s value in O(1), and the order slice disappears entirely because the
list is the order. Put builds a node, links it at the front with addFront, and
records it in the map. When the cache is full, removeTail unlinks tail.prev - the
node furthest from the front - and deletes it from the map.
Because new nodes go to the front and eviction takes the back, the front-to-back
order is newest-to-oldest, so Order now walks tail to head to report the
eviction victim first - and that keeps its results identical to the FIFO cache:
oldest first, evict the oldest. A Get still does not move anything and an update
still stays in place; the behaviour is unchanged, but every operation is now
constant time. That reordering - making a Get promote its node - is the single
change that turns this FIFO cache into an LRU cache, and it is the very next lesson.
// data now maps key -> *node; the order slice is gone.func (c *LRU) Put(key, val int) {if n, ok := c.data[key]; ok { n.val = val; return } // update in place, no move yetif len(c.data) >= c.cap { c.removeTail() }n := &node{key: key, val: val}c.addFront(n); c.data[key] = n}func (c *LRU) removeTail() { lru := c.tail.prev; c.remove(lru); delete(c.data, lru.key) }func (c *LRU) Get(key int) (int, bool) {if n, ok := c.data[key]; ok { return n.val, true }return 0, false}// Order now walks tail -> head so the eviction victim comes first.