目录

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 抛砖引玉的更多相关文章

  1. 从 LRU Cache 带你看面试的本质

    前言 大家好,这里是<齐姐聊算法>系列之 LRU 问题. 在讲这道题之前,我想先聊聊「技术面试究竟是在考什么」这个问题. 技术面试究竟在考什么 在人人都知道刷题的今天,面试官也都知道大家会 ...

  2. [LeetCode] LRU Cache 最近最少使用页面置换缓存器

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...

  3. 【leetcode】LRU Cache

    题目简述: Design and implement a data structure for Least Recently Used (LRU) cache. It should support t ...

  4. LeetCode:LRU Cache

    题目大意:设计一个用于LRU cache算法的数据结构. 题目链接.关于LRU的基本知识可参考here 分析:为了保持cache的性能,使查找,插入,删除都有较高的性能,我们使用双向链表(std::l ...

  5. LRU Cache实现

    最近在看Leveldb源码,里面用到LRU(Least Recently Used)缓存,所以自己动手来实现一下.LRU Cache通常实现方式为Hash Map + Double Linked Li ...

  6. 【leetcode】LRU Cache(hard)★

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...

  7. [LintCode] LRU Cache 缓存器

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...

  8. LRU Cache [LeetCode]

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...

  9. 43. Merge Sorted Array && LRU Cache

    Merge Sorted Array OJ: https://oj.leetcode.com/problems/merge-sorted-array/ Given two sorted integer ...

随机推荐

  1. Keras神经网络集成技术

    Keras神经网络集成技术 create_keras_neuropod 将Keras模型打包为神经网络集成包.目前,上文已经支持TensorFlow后端. create_keras_neuropod( ...

  2. 循环IRNNv2Layer实现

    循环IRNNv2Layer实现 IRNNv2Layer实现循环层,例如循环神经网络(RNN),门控循环单元(GRU)和长期短期记忆(LSTM).支持的类型为RNN,GRU和LSTM.它执行循环操作,该 ...

  3. 工作中,如何衡量一个人的 JavaScript 编码水平?

    1.立即执行函数 立即执行函数,即Immediately Invoked Function Expression (IIFE),正如它的名字,就是创建函数的同时立即执行.它没有绑定任何事件,也无需等待 ...

  4. noip2013 总结

    转圈游戏 题目 n 个小伙伴(编号从 0 到 n-1)围坐一圈玩游戏.按照顺时针方向给 n 个位置编号,从0 到 n-1.最初,第 0 号小伙伴在第 0 号位置,第 1 号小伙伴在第 1 号位置,-- ...

  5. 怎样用好PS中的钢笔工具(附练习钢笔工具网站)

    想要在PS中得心应手的的描绘出自己想要的线条(也就是路径),就需要对[钢笔工具]有一个充分的理解. [钢笔工具]绘出来的线条全部都是贝赛尔曲线,所以你在学习[钢笔工具]之前,要补习一下贝赛尔曲线的常识 ...

  6. SDLC开发安全流程

    2020年12月9日跟内部小伙伴分享SDLC流程及安全的一些思考,简单画了一个图,时间过得太快,记录一下 参考 https://blog.csdn.net/liqiuman180688/article ...

  7. 友华新光猫PT924G破解telnet之路

    最近去找电信要了个新的千兆光猫(电信宽带300兆配100兆光猫真鸡贼),背后一看不是华为了,是友华PT924G,在http://192.168.1.1:8080/里看到了熟悉的电信界面 用teleco ...

  8. CRM系统不仅给企业带来更多收益而且提升销售效率

    将客户信息记录在CRM系统的数据库中,同时共享沟通数据给售前.售后.SDR等上下游,客户资源还能够按照分配规则分配给适合的销售人员,帮助更快成单.全面使用CRM系统会给企业带来更多业绩. 1.全方位客 ...

  9. 无法push项目到gitlab的解决方案

    gitlab项目组下创建项目 $ git push -u git@192.168.101.129:/DrvOps/Dev_Test : 报错信息如下: remote: ================ ...

  10. CentOS-yum安装chrome+chromeDriver+xvfb

    安装chrome 创建yum源文件 $ vim /etc/yum.repos.d/google-chrome.repo [google-chrome] name=google-chrome baseu ...