LRU Cache

LRU (Least Recently Used) is a cache eviction strategy that optimizes for recency: when the cache reaches its capacity, it evicts the entry that hasn’t been accessed for the longest time. A typical LRU implementation combines two data structures:

Add / Put

When adding a key: If the key already exists, update its value and move the corresponding node to the front of the list (marking it as most recently used).

If the key is new, insert it at the front and store its list node in the map. After insertion, if the cache exceeds its capacity, evict the least recently used entry (the node at the back of the list) and remove it from both the list and the map.

Get

When retrieving a key: If it exists, move it to the front of the list and return its value. If it doesn’t exist, return an empty value. Below is a sample implementation in Go.

package main

import (
	"container/list"
	"fmt"
)

type entry struct {
	key, value string
}

type Cache struct {
	mm       map[string]*list.Element
	ll       *list.List
	capacity int
}

func NewCache(capacity int) *Cache {
	return &Cache{
		mm:       make(map[string]*list.Element),
		ll:       list.New(),
		capacity: capacity,
	}
}

// Add inserts or updates key/value and marks it as most-recently-used.
func (c *Cache) Add(key, value string) {
	if c.capacity <= 0 {
		return // treat as always-empty cache
	}

	// Update existing
	if el, ok := c.mm[key]; ok {
		el.Value.(*entry).value = value
		c.ll.MoveToFront(el)
		return
	}

	// Insert new at front
	el := c.ll.PushFront(&entry{key: key, value: value})
	c.mm[key] = el

	// Evict if over capacity
	if len(c.mm) > c.capacity {
		lru := c.ll.Back()
		if lru != nil {
			ent := lru.Value.(*entry)
			delete(c.mm, ent.key)
			c.ll.Remove(lru)
		}
	}
}

// Get returns (value, true) if present; otherwise ("", false).
// Accessing a key marks it as most-recently-used.
func (c *Cache) Get(key string) (string, bool) {
	if el, ok := c.mm[key]; ok {
		c.ll.MoveToFront(el)
		return el.Value.(*entry).value, true
	}
	return "", false
}

func (c *Cache) Size() int {
	return len(c.mm)
}

func main() {
	lru := NewCache(4)
	lru.Add("1", "1")
	lru.Add("2", "2")
	lru.Add("3", "3")
	lru.Add("4", "4")

	lru.Add("5", "5") // evicts "1"
	lru.Add("5", "6") // update, stays MRU
	lru.Add("7", "7") // evicts "2"

	if _, ok := lru.Get("1"); !ok {
		fmt.Println("1 is evicted")
	}

	if v, ok := lru.Get("2"); ok {
		fmt.Println("2 =", v)
	} else {
		fmt.Println("2 is evicted")
	}

	fmt.Println("size =", lru.Size())
}