AlgoMaster Logo

Design LRU Cache

Ashish

Ashish Pratap Singh

What is an LRU Cache?

LRU stands for Least Recently Used. LRU Cache is a type of cache replacement policy that evicts the least recently accessed item when the cache reaches its capacity.

In performance-critical systems (like web servers, databases, or OS memory management), caching helps avoid expensive computations or repeated data fetching. But cache memory is limited so when it's full, we need a policy to decide which item to remove.

LRU chooses the least recently accessed item, based on the assumption that:

“If you haven’t used something for a while, you probably won’t need it soon.”

LRU Cache

The LRU strategy is both intuitive and effective. It reflects real-world usage patterns. We tend to access the same small subset of items frequently, and rarely go back to older, untouched entries.

This makes LRU a popular default caching policy in many systems where speed and memory efficiency are critical.

Example

Suppose we have an LRU cache with capacity = 3, and we perform the following operations:

Final cache state: {3:C, 1:A, 4:D} (in order of usage from least to most recent)

In this chapter, we will explore the low level design of a LRU cache.

Lets start by clarifying the requirements:

1. Clarifying Requirements

Before starting the design, it's important to ask thoughtful questions to uncover hidden assumptions, clarify ambiguities, and define the system's scope more precisely.

Here is an example of how a discussion between the candidate and the interviewer might unfold:

After gathering the details, we can summarize the key system requirements.

1.1 Functional Requirements

  • Support get(key) operation: returns the value if the key exists, otherwise returns null or -1
  • Support put(key, value) operation: inserts a new key-value pair or updates the value of an existing key
  • If the cache exceeds its capacity, it should automatically evict the least recently used item.
  • Both get and put operations should update the recency of the accessed or inserted item.
  • Keys and values should be generic (e.g., <K, V>), provided the keys are hashable.

1.2 Non-Functional Requirements

  1. Time Complexity: Both get and put operations must run in O(1) time on average.
  2. Thread Safety: The implementation must be thread-safe for use in concurrent environments.
  3. Modularity: The design should follow object-oriented principles with clean separation of responsibilities.
  4. Memory Efficiency: The internal data structures should be optimized for speed and space within the defined constraints.

After the requirements are clear, the next step is to identify the core entities that we will form the foundation of our design.

2. Identifying Core Entities

Premium Content

This content is for premium members only.