Go LRU Cache 抛砖引玉
目录
1. LRU Cache
2. container/list.go
2.1 list 数据结构
2.2 list 使用例子
3. transport.go connLRU
4. 结尾
正文
1. LRU Cache
package cache import (
"container/list"
"sync"
) // entry 存储的实体
type entry struct {
key, val interface{}
} // Cache 缓存结构
type Cache struct {
// m 保证 LRU Cache 访问线程安全
rw sync.RWMutex // max 标识缓存容量的最大值, = 0 标识无限缓存
max int // list 是 entry 循环双向链表
list *list.List // pond entry 缓存池子 key -> entry
pond map[interface{}]*list.Element
} // New 构建 LRU Cache 缓存结构
func New(max int) *Cache {
return &Cache{
max: max,
list: list.New(),
pond: make(map[interface{}]*list.Element),
}
} func (c *Cache) delete(key interface{}) {
element, ok := c.pond[key]
if ok {
delete(c.pond, key)
c.list.Remove(element)
return
}
} // Set 设置缓存
func (c *Cache) Set(key, val interface{}) {
c.rw.Lock()
defer c.rw.Unlock() // 看是否进入删除分支
if val == nil {
c.delete(key)
return
} element, ok := c.pond[key]
if ok {
// 重新设置 value 数据
element.Value.(*entry).val = val
// set key nil exists 进入 update 逻辑
c.list.MoveToFront(element)
return
} // 首次添加
c.pond[key] = c.list.PushFront(&entry{key, val}) // 数据过多, 删除尾巴数据
if c.list.Len() > c.max && c.max > 0 {
delete(c.pond, c.list.Remove(c.list.Back()).(*entry).key)
}
} // Get 获取缓存
func (c *Cache) Get(key interface{}) (val interface{}, ok bool) {
c.rw.RLock()
defer c.rw.RUnlock() if element, ok := c.pond[key]; ok {
// 获取指定缓存值
val, ok = element.Value.(*entry).val, true
// 调整缓存热点
c.list.MoveToFront(element)
}
return
}
原理是 1. RWLock 做线程安全 2. list 双向链表保存时间新老关系 2. map 为了让时间复杂度到 O(1).
使用教程:
// 创建
c := cache.New(1) // 设置
c.Set("123", "123")
c.Set("234", "234") // 使用
fmt.Println(c.Get("123"))
fmt.Println(c.Get("234")) // 删除
c.Set("123", nil)
2. container/list.go
2.1 list 数据结构
上面 LRU Cache 代码中引用了 "container/list" , 简单分析下 list, 加深基础数据结构的了解.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // Package list implements a doubly linked list.
//
// To iterate over a list (where l is a *List):
// for e := l.Front(); e != nil; e = e.Next() {
// // do something with e.Value
// }
//
package list // Element is an element of a linked list.
type Element struct {
// Next and previous pointers in the doubly-linked list of elements.
// To simplify the implementation, internally a list l is implemented
// as a ring, such that &l.root is both the next element of the last
// list element (l.Back()) and the previous element of the first list
// element (l.Front()).
next, prev *Element // The list to which this element belongs.
list *List // The value stored with this element.
Value interface{}
} // Next returns the next list element or nil.
func (e *Element) Next() *Element {
if p := e.next; e.list != nil && p != &e.list.root {
return p
}
return nil
} // Prev returns the previous list element or nil.
func (e *Element) Prev() *Element {
if p := e.prev; e.list != nil && p != &e.list.root {
return p
}
return nil
} // List represents a doubly linked list.
// The zero value for List is an empty list ready to use.
type List struct {
root Element // sentinel list element, only &root, root.prev, and root.next are used
len int // current list length excluding (this) sentinel element
}
它是个特殊循环双向链表数据结构, 特殊之处在于 Element::List 指向头结点(root list).

关于业务 list.go 具体实现部分我们不表.
2.2 list 使用例子
func Test_List_Demo(t *testing.T) {
// Persion 普通人
type Persion struct {
Name string
Age int
}
pers := list.New()
// 链表数据填充
pers.PushBack(&Persion{Name: "wang", Age: 31})
pers.PushFront(&Persion{Name: "zhi", Age: 31})
fmt.Printf("List Len() = %d\n", pers.Len())
if pers.Len() != 2 {
t.Fatal("pers.Len() != 2 data faild")
}
// 开始遍历数据
for element := pers.Front(); element != nil; element = element.Next() {
per, ok := element.Value.(*Persion)
if !ok {
panic(fmt.Sprint("Persion list faild", element.Value))
}
fmt.Println(per)
}
// 数据删除
for element := pers.Front(); element != nil; {
next := element.Next()
pers.Remove(element)
element = next
}
fmt.Printf("List Len() = %d\n", pers.Len())
if pers.Len() != 0 {
t.Fatal("pers.Len() != 0 data faild")
}
}
单元测试结果:
Running tool: /usr/local/go/bin/go test -timeout 30s -run ^Test_List_Demo$ demo/src/container/list -v -count=1 === RUN Test_List_Demo
List Len() = 2
&{zhi 31}
&{wang 31}
List Len() = 0
--- PASS: Test_List_Demo (0.00s)
PASS
ok demo/src/container/list 0.002s
3. transport.go connLRU
抛一段 Go 源码中一处应用, 小学以小用
//
// src/net/http/transport.go
// // persistConn wraps a connection, usually a persistent one
// (but may be used for non-keep-alive requests as well)
type persistConn struct {
...
..
.
} type connLRU struct {
ll *list.List // list.Element.Value type is of *persistConn
m map[*persistConn]*list.Element
} // add adds pc to the head of the linked list.
func (cl *connLRU) add(pc *persistConn) {
if cl.ll == nil {
cl.ll = list.New()
cl.m = make(map[*persistConn]*list.Element)
}
ele := cl.ll.PushFront(pc)
if _, ok := cl.m[pc]; ok {
panic("persistConn was already in LRU")
}
cl.m[pc] = ele
} func (cl *connLRU) removeOldest() *persistConn {
ele := cl.ll.Back()
pc := ele.Value.(*persistConn)
cl.ll.Remove(ele)
delete(cl.m, pc)
return pc
} // remove removes pc from cl.
func (cl *connLRU) remove(pc *persistConn) {
if ele, ok := cl.m[pc]; ok {
cl.ll.Remove(ele)
delete(cl.m, pc)
}
} // len returns the number of items in the cache.
func (cl *connLRU) len() int {
return len(cl.m)
}
4. 结尾
很多代码, 很多事情也都平淡无奇, 但凡事种种都离不开用心, 反复琢磨 ~ 方能长久
欢迎批评指正交流 ~ good luckly ~
Go LRU Cache 抛砖引玉的更多相关文章
- 从 LRU Cache 带你看面试的本质
前言 大家好,这里是<齐姐聊算法>系列之 LRU 问题. 在讲这道题之前,我想先聊聊「技术面试究竟是在考什么」这个问题. 技术面试究竟在考什么 在人人都知道刷题的今天,面试官也都知道大家会 ...
- [LeetCode] LRU Cache 最近最少使用页面置换缓存器
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...
- 【leetcode】LRU Cache
题目简述: Design and implement a data structure for Least Recently Used (LRU) cache. It should support t ...
- LeetCode:LRU Cache
题目大意:设计一个用于LRU cache算法的数据结构. 题目链接.关于LRU的基本知识可参考here 分析:为了保持cache的性能,使查找,插入,删除都有较高的性能,我们使用双向链表(std::l ...
- LRU Cache实现
最近在看Leveldb源码,里面用到LRU(Least Recently Used)缓存,所以自己动手来实现一下.LRU Cache通常实现方式为Hash Map + Double Linked Li ...
- 【leetcode】LRU Cache(hard)★
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...
- [LintCode] LRU Cache 缓存器
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...
- LRU Cache [LeetCode]
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...
- 43. Merge Sorted Array && LRU Cache
Merge Sorted Array OJ: https://oj.leetcode.com/problems/merge-sorted-array/ Given two sorted integer ...
随机推荐
- MEMS传感器作为变革的驱动力
MEMS sensors as drivers for change 物联网(IoT)正在改变与周围世界互动的方式.每个人,每件事,都是相互联系的,很快就会相互联系.微机电系统(MEMS)设备和传感器 ...
- halcon——缺陷检测常用方法总结(光度立体)
引言 机器视觉中缺陷检测分为一下几种: blob+特征(官方示例surface_scratch.hdev) blob+差分+特征(官方示例pcb_inspection.hdev) 光度立体 特征训练 ...
- Django(59)验证和授权
验证和授权概述 Django有一个内置的授权系统.他用来处理用户.分组.权限以及基于cookie的会话系统.Django的授权系统包括验证和授权两个部分.验证是验证这个用户是否是他声称的人(比如用 ...
- vulhub-struct2-s2-005
0x00 漏洞原理 s2-005漏洞的起源源于S2-003(受影响版本: 低于Struts 2.0.12),struts2会将http的每个参数名解析为OGNL语句执行(可理解为java代码).O ...
- 尚硅谷Java——宋红康笔记【day1-day5】
day1 注释 1.java规范的三种注释方式: 单行注释 多行注释 文档注释(java特有) 2. 单行注释和多行注释的作用: ① 对所写的程序进行解释说明,增强可读性.方便自己,方便别人 ② 调试 ...
- Padavan安装使用ZeroTier实现组建虚拟局域网的方法
首先到这个网站ZeroTier – Global Area Networking注册登陆,注册及创建网络的过程可以参考网上及其他UP主的教程,我就不重复了. 本篇主要讲述的是hiboy大佬编译的固件在 ...
- 【Azure 机器人】微软Azure Bot 编辑器系列(4) : 使用语言生成功能[LG: Language Generation] (The Bot Framework Composer tutorials)
欢迎来到微软机器人编辑器使用教程,从这里开始,创建一个简单的机器人. 在该系列文章中,每一篇都将通过添加更多的功能来构建机器人.当完成教程中的全部内容后,你将成功的创建一个天气机器人(Weather ...
- 【linux】驱动-15-定时器
目录 前言 15. 定时器 15.1 内核函数汇总 15.2 内核滴答 15.3 相关结构体 15.4 setup_timer() 设置定时器 15.5 add_timer() 向内核添加定时器 15 ...
- asp.net core 实现 face recognition 使用 tensorflowjs(源代码)
功能描述 上传照片文件名及是系统要识别标签或是照片的名称(人物标识) 提取照片脸部特征值(调用 facemesh模型) 保存特征值添加样本(调用 knnClassifier) 测试上传的图片是否识别正 ...
- 【spring源码系列】之【Bean的生命周期】
为源码付出的每一分努力都不会白费. 1. Bean的实例化概述 前一篇分析了BeanDefinition的封装过程,最终将beanName与BeanDefinition以一对一映射关系放到beanDe ...