utils/LRUCache.tstypescript
/**
* Least recently used cache of a limited number of entries. Map keeps the insertion order,
* so the first key is always the least recently used one.
* @class
* @param {number} capacity - Maximal number of entries.
* @param {Function} [onEvict] - Called with the entries removed to free the place for new ones.
*/
export class LRUCache<K, V> {
public readonly capacity: number;
protected _map: Map<K, V>;
protected _onEvict: ((key: K, value: V) => void) | null;
constructor(capacity: number, onEvict?: (key: K, value: V) => void) {
if (!(capacity >= 1)) {
throw new Error(`LRUCache: capacity must be at least 1, ${capacity} is given`);
}
this.capacity = Math.floor(capacity);
this._map = new Map();
this._onEvict = onEvict || null;
}
public get size(): number {
return this._map.size;
}
/**
* Returns the value and makes it the most recently used.
*/
public get(key: K): V | undefined {
if (!this._map.has(key)) {
return undefined;
}
let value = this._map.get(key)!;
this._map.delete(key);
this._map.set(key, value);
return value;
}
/**
* Returns the value without making it the most recently used.
*/
public peek(key: K): V | undefined {
return this._map.get(key);
}
/**
* Sets the most recently used value, the least recently used ones over the capacity are evicted.
*/
public set(key: K, value: V) {
this._map.delete(key);
this._map.set(key, value);
while (this._map.size > this.capacity) {
let oldest = this._map.keys().next().value as K,
oldestValue = this._map.get(oldest)!;
this._map.delete(oldest);
this._onEvict && this._onEvict(oldest, oldestValue);
}
}
/**
* Returns the values from the least to the most recently used, without making them used.
*/
public values(): IterableIterator<V> {
return this._map.values();
}
public has(key: K): boolean {
return this._map.has(key);
}
/**
* Removes the entry, not counted as an eviction.
*/
public delete(key: K): boolean {
return this._map.delete(key);
}
/**
* Removes all the entries, not counted as evictions.
*/
public clear() {
this._map.clear();
}
}