使用go語言的list實現一個簡單的LRU緩存


package main;

import (
	"container/list"
	"errors"
	"sync"
	"fmt"
	"encoding/json"
)

//LRU(Least recently used)最近最少使用,算法根據數據的歷史訪問記錄來進行淘汰數據
//核心思想是"如果數據最近被訪問過,那么將來被訪問的幾率也更高"
//常見的實現方式是用一個鏈表保存數據
//1. 新數據插入到鏈表頭部
//2. 每當緩存命中(即緩存數據被訪問),則將數據移到鏈表頭部
//3. 當鏈表滿的時候,將鏈表尾部的數據丟棄

type cacheItem struct {
	Key string;
	Val interface{};
}

type LRU struct {
	//最大存儲數量
	maxNum int;
	//當前存儲數量
	curNum int;
	//鎖,保證數據一致性
	mutex  sync.Mutex;
	//鏈表
	data   *list.List;
}

//添加數據
func (l *LRU) add(key string, value interface{}) error {
	//判斷key是否存在
	if e, _ := l.exist(key); e {
		return errors.New(key + "已存在");
	}
	//判斷當前存儲數量與最大存儲數量
	if l.maxNum == l.curNum {
		//鏈表已滿,則刪除鏈表尾部元素
		l.clear();
	}
	l.mutex.Lock();
	l.curNum++;
	//json序列化數據
	data, _ := json.Marshal(cacheItem{key, value});
	//把數據保存到鏈表頭部
	l.data.PushFront(data);
	l.mutex.Unlock();
	return nil;
}

//設置數據
func (l *LRU) set(key string, value interface{}) error {
	e, item := l.exist(key);
	if !e {
		return l.add(key, value);
	}
	l.mutex.Lock();
	data, _ := json.Marshal(cacheItem{key, value});
	//設置鏈表元素數據
	item.Value = data;
	l.mutex.Unlock();
	return nil;
}

//清理數據
func (l *LRU) clear() interface{} {
	l.mutex.Lock();
	l.curNum--;
	//刪除鏈表尾部元素
	v := l.data.Remove(l.data.Back());
	l.mutex.Unlock();
	return v;
}

//獲取數據
func (l *LRU) get(key string) interface{} {
	e, item := l.exist(key);
	if !e {
		return nil;
	}
	l.mutex.Lock();
	//數據被訪問,則把元素移動到鏈表頭部
	l.data.MoveToFront(item);
	l.mutex.Unlock();
	var data cacheItem;
	json.Unmarshal(item.Value.([]byte), &data);
	return data.Val;
}

//刪除數據
func (l *LRU) del(key string) error {
	e, item := l.exist(key);
	if !e {
		return errors.New(key + "不存在");
	}
	l.mutex.Lock();
	l.curNum--;
	//刪除鏈表元素
	l.data.Remove(item);
	l.mutex.Unlock();
	return nil;
}

//判斷是否存在
func (l *LRU) exist(key string) (bool, *list.Element) {
	var data cacheItem;
	//循環鏈表,判斷key是否存在
	for v := l.data.Front(); v != nil; v = v.Next() {
		json.Unmarshal(v.Value.([]byte), &data);
		if key == data.Key {
			return true, v;
		}
	}
	return false, nil;
}

//返回長度
func (l *LRU) len() int {
	return l.curNum;
}

//打印鏈表
func (l *LRU) print() {
	var data cacheItem;
	for v := l.data.Front(); v != nil; v = v.Next() {
		json.Unmarshal(v.Value.([]byte), &data);
		fmt.Println("key:", data.Key, " value:", data.Val);
	}
}

//創建一個新的LRU
func LRUNew(maxNum int) *LRU {
	return &LRU{
		maxNum: maxNum,
		curNum: 0,
		mutex:  sync.Mutex{},
		data:   list.New(),
	};
}

func main() {
	lru := LRUNew(5);
	lru.add("1111", 1111);
	lru.add("2222", 2222);
	lru.add("3333", 3333);
	lru.add("4444", 4444);
	lru.add("5555", 5555);
	lru.print();
	//get成功后,可以看到3333元素移動到了鏈表頭
	fmt.Println(lru.get("3333"));
	lru.print();
	//再次添加元素,如果超過最大數量,則刪除鏈表尾部元素,將新元素添加到鏈表頭
	lru.add("6666", 6666);
	lru.print();
	lru.del("4444");
	lru.print();
	lru.set("2222", "242424");
	lru.print();
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM