LRU cache
Java
cache implementation
data structures
programming tutorial

How would you implement an LRU cache in Java?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Least Recently Used (LRU) cache is a caching strategy that ensures that when the cache reaches its maximum capacity, the least recently accessed items are discarded first to make room for new entries. The LRU strategy provides an efficient way to manage memory and optimize performance in applications where resources are limited. In this article, we'll explore how to implement an LRU cache in Java, providing technical explanations and coding examples.

Key Concepts of LRU Cache

An LRU cache maintains the following properties:

  • Capacity: The maximum number of items the cache can hold.
  • Eviction Policy: The least recently used entry is removed when the cache reaches its capacity.
  • Fast Access: The cache should provide fast access (ideally O(1) time complexity) for both get and put operations.

Implementation Using LinkedHashMap

Java's LinkedHashMap class provides an elegant way to implement an LRU cache by maintaining a doubly-linked list across all of its entries. Here's how it can be done:

Step-by-Step Guide

Step 1: Define the LRU Cache Class

java
1import java.util.LinkedHashMap;
2import java.util.Map;
3
4public class LRUCache<K, V> extends LinkedHashMap<K, V> {
5    private final int capacity;
6
7    public LRUCache(int capacity) {
8        super(capacity, 0.75f, true);
9        this.capacity = capacity;
10    }
11    
12    @Override
13    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
14        return size() > capacity;
15    }
16}

Key Points:

  • Initialization: The constructor sets up the LinkedHashMap with three parameters: initial capacity, load factor, and ordering mode. Specifying true for ordering mode makes it access-order, turning LinkedHashMap into an LRU cache.
  • Eviction Policy: Override the removeEldestEntry method to specify that the eldest entry should be removed if the current size exceeds the predefined capacity.

Step 2: Demonstrate Usage

java
1public class LRUCacheDemo {
2    public static void main(String[] args) {
3        LRUCache<Integer, String> cache = new LRUCache<>(3);
4        
5        cache.put(1, "A");
6        cache.put(2, "B");
7        cache.put(3, "C");
8        System.out.println("Cache: " + cache);
9        
10        cache.get(1);
11        System.out.println("Cache after accessing key 1: " + cache);
12        
13        cache.put(4, "D");
14        System.out.println("Cache after adding key 4 (and evicting the least used key): " + cache);
15    }
16}

Explanation:

  • Initialization: An LRUCache instance is created with a capacity of 3.
  • Operations: We add items, access them, and continue to exceed the capacity to observe eviction of the least-recently used item.

Considerations

Performance

Utilizing LinkedHashMap, which leverages a hash table and a doubly-linked list, ensures that the operations get and put run in constant time, O(1)O(1).

Thread Safety

Diving deeper into real-world applications, consider thread safety. For thread-safe implementations, you can wrap the LRUCache with Collections.synchronizedMap. However, for high-concurrency environments, using ConcurrentHashMap combined with custom logic might be necessary.

Memory Management

The cache should be mindful of memory consumption, particularly in memory-constrained environments. The LRU cache implicitly manages memory by evicting entries, but ensure the cache size is appropriate for the application's constraints.

Summary Table

FeatureLinkedHashMap LRUCache
Time ComplexityGet/Puts: O(1)
Eviction StrategyRemoves least recently used item
Thread SafetyNon-thread-safe; synchronization needed
Use CaseEfficient in-memory key-value storage

Conclusion

Implementing an LRU cache using Java's LinkedHashMap is both efficient and straightforward. By understanding the underlying mechanics of LinkedHashMap, such as ordering modes and the remove-eldest-entry policy, a proficient and effective LRU cache can be developed in Java. Additionally, for applications requiring higher concurrency, further enhancements may be warranted to ensure thread safety and optimal performance.


Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.