Skip to content

Commit

Permalink
Add method GetAndAdd to LRU Cache
Browse files Browse the repository at this point in the history
  • Loading branch information
vasayxtx committed Dec 27, 2020
1 parent eb52994 commit eb1f8f2
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 5 deletions.
8 changes: 8 additions & 0 deletions lru.go
Expand Up @@ -53,6 +53,14 @@ func (c *Cache) Get(key interface{}) (value interface{}, ok bool) {
return value, ok
}

// GetAndAdd returns the previous key's value from the cache and adds a new one.
func (c *Cache) GetAndAdd(key, value interface{}) (previous interface{}, ok, evicted bool) {
c.lock.Lock()
previous, ok, evicted = c.lru.GetAndAdd(key, value)
c.lock.Unlock()
return
}

// Contains checks if a key is in the cache, without updating the
// recent-ness or deleting it for being stale.
func (c *Cache) Contains(key interface{}) bool {
Expand Down
19 changes: 19 additions & 0 deletions lru_test.go
Expand Up @@ -152,6 +152,25 @@ func TestLRUAdd(t *testing.T) {
}
}

func TestLRU_GetAndAdd(t *testing.T) {
l, err := New(1)
if err != nil {
t.Fatalf("err: %v", err)
}
if previous, ok, evicted := l.GetAndAdd(1, 1); ok || evicted {
t.Errorf("1 should not be found, eviction should not be: %v, %v, %v", previous, ok, evicted)
}
if previous, ok, evicted := l.GetAndAdd(1, 10); !ok || previous != 1 || evicted {
t.Errorf("1 should be returned as previous value, eviction should not be: %v, %v, %v", previous, ok, evicted)
}
if v, ok := l.Get(1); !ok || v != 10 {
t.Errorf("1 should be set to 10: %v, %v", v, ok)
}
if previous, ok, evicted := l.GetAndAdd(2, 2); ok || !evicted {
t.Errorf("2 should not be found, eviction should be: %v, %v, %v", previous, ok, evicted)
}
}

// test that Contains doesn't update recent-ness
func TestLRUContains(t *testing.T) {
l, err := New(2)
Expand Down
17 changes: 12 additions & 5 deletions simplelru/lru.go
Expand Up @@ -47,26 +47,33 @@ func (c *LRU) Purge() {
c.evictList.Init()
}

// Add adds a value to the cache. Returns true if an eviction occurred.
// Add adds a value to the cache. Returns true if an eviction occurred.
func (c *LRU) Add(key, value interface{}) (evicted bool) {
_, _, evicted = c.GetAndAdd(key, value)
return
}

// GetAndAdd returns the previous key's value from the cache and adds a new one.
func (c *LRU) GetAndAdd(key, value interface{}) (previous interface{}, ok, evicted bool) {
// Check for existing item
if ent, ok := c.items[key]; ok {
c.evictList.MoveToFront(ent)
previous = ent.Value.(*entry).value
ent.Value.(*entry).value = value
return false
return previous, true, false
}

// Add new item
ent := &entry{key, value}
entry := c.evictList.PushFront(ent)
c.items[key] = entry

evict := c.evictList.Len() > c.size
evicted = c.evictList.Len() > c.size
// Verify size not exceeded
if evict {
if evicted {
c.removeOldest()
}
return evict
return nil, false, evicted
}

// Get looks up a key's value from the cache.
Expand Down
3 changes: 3 additions & 0 deletions simplelru/lru_interface.go
Expand Up @@ -11,6 +11,9 @@ type LRUCache interface {
// updates the "recently used"-ness of the key. #value, isFound
Get(key interface{}) (value interface{}, ok bool)

// Returns the previous key's value from the cache and adds a new one.
GetAndAdd(key, value interface{}) (previous interface{}, ok, evicted bool)

// Checks if a key exists in cache without updating the recent-ness.
Contains(key interface{}) (ok bool)

Expand Down
19 changes: 19 additions & 0 deletions simplelru/lru_test.go
Expand Up @@ -128,6 +128,25 @@ func TestLRU_Add(t *testing.T) {
}
}

func TestLRU_GetAndAdd(t *testing.T) {
l, err := NewLRU(1, nil)
if err != nil {
t.Fatalf("err: %v", err)
}
if previous, ok, evicted := l.GetAndAdd(1, 1); ok || evicted {
t.Errorf("1 should not be found, eviction should not be: %v, %v, %v", previous, ok, evicted)
}
if previous, ok, evicted := l.GetAndAdd(1, 10); !ok || previous != 1 || evicted {
t.Errorf("1 should be returned as previous value, eviction should not be: %v, %v, %v", previous, ok, evicted)
}
if v, ok := l.Get(1); !ok || v != 10 {
t.Errorf("1 should be set to 10: %v, %v", v, ok)
}
if previous, ok, evicted := l.GetAndAdd(2, 2); ok || !evicted {
t.Errorf("2 should not be found, eviction should be: %v, %v, %v", previous, ok, evicted)
}
}

// Test that Contains doesn't update recent-ness
func TestLRU_Contains(t *testing.T) {
l, err := NewLRU(2, nil)
Expand Down

0 comments on commit eb1f8f2

Please sign in to comment.