Full LRU/FIFO Cache Implementation in Golang
It should support Set and Get Operation, O(1) Time Complexity for both Set and Get.
Here is the full working code in go programming language if anyone is interested.
doublylinklist.go
package main
import "fmt"
type node struct {
key string
value string
prev *node
next *node
}
type doublyLinkedList struct {
len int
tail *node
head *node
}
func initDoublyList() *doublyLinkedList {
return &doublyLinkedList{}
}
func (d *doublyLinkedList) AddToFront(key, value string) {
newNode := &node{
key: key,
value: value,
}
if d.head == nil {
d.head = newNode
d.tail = newNode
} else {
newNode.next = d.head
d.head.prev = newNode
d.head = newNode
}
d.len++
return
}
func (d *doublyLinkedList) RemoveFromFront() {
if d.head == nil {
return
} else if d.head == d.tail {
d.head = nil
d.tail = nil
} else {
d.head = d.head.next
}
d.len--
}
func (d *doublyLinkedList) AddToEnd(node *node) {
newNode := node
if d.head == nil {
d.head = newNode
d.tail = newNode
} else {
currentNode := d.head
for currentNode.next != nil {
currentNode = currentNode.next
}
newNode.prev = currentNode
currentNode.next = newNode
d.tail = newNode
}
d.len++
}
func (d *doublyLinkedList) Front() *node {
return d.head
}
func (d *doublyLinkedList) MoveNodeToEnd(node *node) {
prev := node.prev
next := node.next
if prev != nil {
prev.next = next
}
if next != nil {
next.prev = prev
}
if d.tail == node {
d.tail = prev
}
if d.head == node {
d.head = next
}
node.next = nil
node.prev = nil
d.len--
d.AddToEnd(node)
}
func (d *doublyLinkedList) TraverseForward() error {
if d.head == nil {
return fmt.Errorf("TraverseError: List is empty")
}
temp := d.head
for temp != nil {
fmt.Printf("key = %v, value = %v, prev = %v, next = %v\n", temp.key, temp.value, temp.prev, temp.next)
temp = temp.next
}
fmt.Println()
return nil
}
func (d *doublyLinkedList) Size() int {
return d.len
}
evictionAlgorithm.go
package main
type evictionAlgo interface {
evict(c *Cache) string
get(node *node, c *Cache)
set(node *node, c *Cache)
set_overwrite(node *node, value string, c *Cache)
}
func createEvictioAlgo(algoType string) evictionAlgo {
if algoType == "fifo" {
return &fifo{}
} else if algoType == "lru" {
return &lru{}
}
return nil
}
lru.go
package main
import "fmt"
type lru struct {
}
func (l *lru) evict(c *Cache) string {
key := c.doublyLinkedList.Front().key
fmt.Printf("Evicting by lru strtegy. Evicted Node Key: %s: ", key)
c.doublyLinkedList.RemoveFromFront()
return key
}
func (l *lru) get(node *node, c *Cache) {
fmt.Println("Shuffling doubly linked list due to get operation")
c.doublyLinkedList.MoveNodeToEnd(node)
}
func (l *lru) set(node *node, c *Cache) {
fmt.Println("Shuffling doubly linked list due to set operation")
c.doublyLinkedList.AddToEnd(node)
}
func (l *lru) set_overwrite(node *node, value string, c *Cache) {
fmt.Println("Shuffling doubly linked list due to set_overwrite operation")
node.value = value
c.doublyLinkedList.MoveNodeToEnd(node)
}
fifo.go
package main
import "fmt"
type fifo struct {
}
func (l *fifo) evict(c *Cache) string {
fmt.Println("Evicting by fifo strtegy")
key := c.doublyLinkedList.Front().key
c.doublyLinkedList.RemoveFromFront()
return key
}
func (l *fifo) get(node *node, c *Cache) {
fmt.Println("Shuffling doubly linked list due to get operation")
}
func (l *fifo) set(node *node, c *Cache) {
fmt.Println("Shuffling doubly linked list due to set operation")
c.doublyLinkedList.AddToEnd(node)
}
func (l *fifo) set_overwrite(node *node, value string, c *Cache) {
fmt.Println("Shuffling doubly linked list due to set_overwrite operation")
}
cache.go
package main
import "fmt"
type Cache struct {
doublyLinkedList *doublyLinkedList
storage map[string]*node
evictionAlgo evictionAlgo
capacity int
maxCapacity int
}
func initCache(evictionAlgo evictionAlgo, maxCapacity int) Cache {
storage := make(map[string]*node)
return Cache{
doublyLinkedList: &doublyLinkedList{},
storage: storage,
evictionAlgo: evictionAlgo,
capacity: 0,
maxCapacity: maxCapacity,
}
}
func (this *Cache) setEvictionAlgo(e evictionAlgo) {
this.evictionAlgo = e
}
func (this *Cache) set(key, value string) {
node_ptr, ok := this.storage[key]
if ok {
this.evictionAlgo.set_overwrite(node_ptr, value, this)
return
}
if this.capacity == this.maxCapacity {
evictedKey := this.evict()
delete(this.storage, evictedKey)
}
node := &node{key: key, value: value}
this.storage[key] = node
this.evictionAlgo.set(node, this)
this.capacity++
}
func (this *Cache) get(key string) string {
node_ptr, ok := this.storage[key]
if ok {
this.evictionAlgo.get(node_ptr, this)
return (*node_ptr).value
}
return ""
}
func (this *Cache) evict() string {
key := this.evictionAlgo.evict(this)
this.capacity--
return key
}
func (this *Cache) print() {
for k, v := range this.storage {
fmt.Printf("key :%s value: %s\n", k, (*v).value)
}
this.doublyLinkedList.TraverseForward()
}
Used in main.go
package main
import "fmt"
func main() {
lru := createEvictioAlgo("lru")
cache := initCache(lru, 3)
cache.set("a", "1")
cache.print()
cache.set("b", "2")
cache.print()
cache.set("c", "3")
cache.print()
value := cache.get("a")
fmt.Printf("key: a, value: %s\n", value)
cache.print()
cache.set("d", "4")
cache.print()
cache.set("e", "5")
cache.print()
}
0
See Also
- LRU Cache Implementation in Golang With container/list
- Golang mod import private package
- Golang mod import local package
- Do net.Conn unit test using net.Pipe in Golang
- Generate User's Avatars with Pure Golang