forked from trekhleb/javascript-algorithms
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add an example of the LRU Cache based on the Map.
- Loading branch information
Showing
4 changed files
with
216 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/* eslint-disable no-restricted-syntax, no-unreachable-loop */ | ||
|
||
/** | ||
* Implementation of the LRU (Least Recently Used) Cache | ||
* based on the (ordered) Map data-structure. | ||
* | ||
* Current implementation allows to have fast O(1) (in average) read and write operations. | ||
* | ||
* At any moment in time the LRU Cache holds not more that "capacity" number of items in it. | ||
*/ | ||
class LRUCacheOnMap { | ||
/** | ||
* Creates a cache instance of a specific capacity. | ||
* @param {number} capacity | ||
*/ | ||
constructor(capacity) { | ||
this.capacity = capacity; // How many items to store in cache at max. | ||
this.items = new Map(); // The ordered hash map of all cached items. | ||
} | ||
|
||
/** | ||
* Returns the cached value by its key. | ||
* Time complexity: O(1) in average. | ||
* @param {string} key | ||
* @returns {any} | ||
*/ | ||
get(key) { | ||
if (!this.items.has(key)) return undefined; | ||
const val = this.items.get(key); | ||
this.items.delete(key); | ||
this.items.set(key, val); | ||
return val; | ||
} | ||
|
||
/** | ||
* Sets the value to cache by its key. | ||
* Time complexity: O(1). | ||
* @param {string} key | ||
* @param {any} val | ||
*/ | ||
set(key, val) { | ||
this.items.delete(key); | ||
this.items.set(key, val); | ||
if (this.items.size > this.capacity) { | ||
for (const headKey of this.items.keys()) { | ||
this.items.delete(headKey); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
|
||
export default LRUCacheOnMap; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
148 changes: 148 additions & 0 deletions
148
src/data-structures/lru-cache/__test__/LRUCacheOnMap.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import LRUCache from '../LRUCacheOnMap'; | ||
|
||
describe('LRUCacheOnMap', () => { | ||
it('should set and get values to and from the cache', () => { | ||
const cache = new LRUCache(100); | ||
expect(cache.get('key-1')).toBeUndefined(); | ||
|
||
cache.set('key-1', 15); | ||
cache.set('key-2', 16); | ||
cache.set('key-3', 17); | ||
expect(cache.get('key-1')).toBe(15); | ||
expect(cache.get('key-2')).toBe(16); | ||
expect(cache.get('key-3')).toBe(17); | ||
expect(cache.get('key-3')).toBe(17); | ||
expect(cache.get('key-2')).toBe(16); | ||
expect(cache.get('key-1')).toBe(15); | ||
|
||
cache.set('key-1', 5); | ||
cache.set('key-2', 6); | ||
cache.set('key-3', 7); | ||
expect(cache.get('key-1')).toBe(5); | ||
expect(cache.get('key-2')).toBe(6); | ||
expect(cache.get('key-3')).toBe(7); | ||
}); | ||
|
||
it('should evict least recently used items from cache with cache size of 1', () => { | ||
const cache = new LRUCache(1); | ||
expect(cache.get('key-1')).toBeUndefined(); | ||
|
||
cache.set('key-1', 15); | ||
expect(cache.get('key-1')).toBe(15); | ||
|
||
cache.set('key-2', 16); | ||
expect(cache.get('key-1')).toBeUndefined(); | ||
expect(cache.get('key-2')).toBe(16); | ||
|
||
cache.set('key-2', 17); | ||
expect(cache.get('key-2')).toBe(17); | ||
|
||
cache.set('key-3', 18); | ||
cache.set('key-4', 19); | ||
expect(cache.get('key-2')).toBeUndefined(); | ||
expect(cache.get('key-3')).toBeUndefined(); | ||
expect(cache.get('key-4')).toBe(19); | ||
}); | ||
|
||
it('should evict least recently used items from cache with cache size of 2', () => { | ||
const cache = new LRUCache(2); | ||
expect(cache.get('key-21')).toBeUndefined(); | ||
|
||
cache.set('key-21', 15); | ||
expect(cache.get('key-21')).toBe(15); | ||
|
||
cache.set('key-22', 16); | ||
expect(cache.get('key-21')).toBe(15); | ||
expect(cache.get('key-22')).toBe(16); | ||
|
||
cache.set('key-22', 17); | ||
expect(cache.get('key-22')).toBe(17); | ||
|
||
cache.set('key-23', 18); | ||
expect(cache.get('key-21')).toBeUndefined(); | ||
expect(cache.get('key-22')).toBe(17); | ||
expect(cache.get('key-23')).toBe(18); | ||
|
||
cache.set('key-24', 19); | ||
expect(cache.get('key-21')).toBeUndefined(); | ||
expect(cache.get('key-22')).toBeUndefined(); | ||
expect(cache.get('key-23')).toBe(18); | ||
expect(cache.get('key-24')).toBe(19); | ||
}); | ||
|
||
it('should evict least recently used items from cache with cache size of 3', () => { | ||
const cache = new LRUCache(3); | ||
|
||
cache.set('key-1', 1); | ||
cache.set('key-2', 2); | ||
cache.set('key-3', 3); | ||
expect(cache.get('key-1')).toBe(1); | ||
expect(cache.get('key-2')).toBe(2); | ||
expect(cache.get('key-3')).toBe(3); | ||
|
||
cache.set('key-3', 4); | ||
expect(cache.get('key-1')).toBe(1); | ||
expect(cache.get('key-2')).toBe(2); | ||
expect(cache.get('key-3')).toBe(4); | ||
|
||
cache.set('key-4', 5); | ||
expect(cache.get('key-1')).toBeUndefined(); | ||
expect(cache.get('key-2')).toBe(2); | ||
expect(cache.get('key-3')).toBe(4); | ||
expect(cache.get('key-4')).toBe(5); | ||
}); | ||
|
||
it('should promote the node while calling set() method', () => { | ||
const cache = new LRUCache(2); | ||
|
||
cache.set('2', 1); | ||
cache.set('1', 1); | ||
cache.set('2', 3); | ||
cache.set('4', 1); | ||
expect(cache.get('1')).toBeUndefined(); | ||
expect(cache.get('2')).toBe(3); | ||
}); | ||
|
||
it('should promote the recently accessed item with cache size of 3', () => { | ||
const cache = new LRUCache(3); | ||
|
||
cache.set('key-1', 1); | ||
cache.set('key-2', 2); | ||
cache.set('key-3', 3); | ||
expect(cache.get('key-1')).toBe(1); | ||
|
||
cache.set('key-4', 4); | ||
expect(cache.get('key-1')).toBe(1); | ||
expect(cache.get('key-3')).toBe(3); | ||
expect(cache.get('key-4')).toBe(4); | ||
expect(cache.get('key-2')).toBeUndefined(); | ||
}); | ||
|
||
it('should promote the recently accessed item with cache size of 4', () => { | ||
const cache = new LRUCache(4); | ||
|
||
cache.set('key-1', 1); | ||
cache.set('key-2', 2); | ||
cache.set('key-3', 3); | ||
cache.set('key-4', 4); | ||
expect(cache.get('key-4')).toBe(4); | ||
expect(cache.get('key-3')).toBe(3); | ||
expect(cache.get('key-2')).toBe(2); | ||
expect(cache.get('key-1')).toBe(1); | ||
|
||
cache.set('key-5', 5); | ||
expect(cache.get('key-1')).toBe(1); | ||
expect(cache.get('key-2')).toBe(2); | ||
expect(cache.get('key-3')).toBe(3); | ||
expect(cache.get('key-4')).toBeUndefined(); | ||
expect(cache.get('key-5')).toBe(5); | ||
|
||
cache.set('key-6', 6); | ||
expect(cache.get('key-1')).toBeUndefined(); | ||
expect(cache.get('key-2')).toBe(2); | ||
expect(cache.get('key-3')).toBe(3); | ||
expect(cache.get('key-4')).toBeUndefined(); | ||
expect(cache.get('key-5')).toBe(5); | ||
expect(cache.get('key-6')).toBe(6); | ||
}); | ||
}); |