← Runnable Projects

阅读中文版本

Least-Recently-Used Cache

A Node module implementing an LRU cache with a fixed capacity, get and set with eviction, and hit and miss statistics.

intermediatejavascriptunverified

Project

node-lru-cache

Entry point: lru.mjs

Install

node --version

Run

node lru.mjs

Test

node --test lru.test.mjs

Expected output

Prints cache hits, misses, and the key that was evicted when capacity was exceeded.

Dependencies

  • node >=20

What it does

Implements an LRU cache where reads refresh recency, using the insertion order of a Map to make eviction O(1).

export class LruCache {
  constructor(capacity) {
    if (!Number.isInteger(capacity) || capacity < 1) throw new Error('capacity must be >= 1');
    this.capacity = capacity;
    this.map = new Map();
    this.hits = 0;
    this.misses = 0;
  }
  get(key) {
    if (!this.map.has(key)) { this.misses++; return undefined; }
    const value = this.map.get(key);
    this.map.delete(key);
    this.map.set(key, value);
    this.hits++;
    return value;
  }
  set(key, value) {
    if (this.map.has(key)) this.map.delete(key);
    this.map.set(key, value);
    if (this.map.size > this.capacity) {
      const oldest = this.map.keys().next().value;
      this.map.delete(oldest);
      return oldest;
    }
    return null;
  }
}

if (import.meta.url === `file://${process.argv[1]}`) {
  const cache = new LruCache(2);
  cache.set('a', 1);
  cache.set('b', 2);
  cache.get('a');          // refresh a
  const evicted = cache.set('c', 3); // evicts b
  console.log('evicted=' + evicted);
  console.log('hits=' + cache.hits + ' misses=' + cache.misses);
}

Statistics are tracked so the demo can show why a cache is working: a high hit rate means the capacity and key choice are doing their job.

Run and verify

node lru.mjs

Run the module directly to execute the demo; importing it as a library skips the demo block.

Expected Output

The demo touches a to refresh it and then inserts c, which should evict b. The console.log output is:

evicted=b
hits=1 misses=0

Reading an existing key counts as a hit and moves it to most-recent, so the next insert evicts the least-recently used key rather than the oldest inserted.

Limitations

The cache holds values in memory and has no time-based expiry; add a timestamp and a TTL check if entries must age out.