目录

1. LRU Cache

2. container/list.go

2.1 list 数据结构

2.2 list 使用例子

3. transport.go connLRU

4. 结尾

正文

1. LRU Cache

  1. package cache
  2.  
  3. import (
  4. "container/list"
  5. "sync"
  6. )
  7.  
  8. // entry 存储的实体
  9. type entry struct {
  10. key, val interface{}
  11. }
  12.  
  13. // Cache 缓存结构
  14. type Cache struct {
  15. // m 保证 LRU Cache 访问线程安全
  16. rw sync.RWMutex
  17.  
  18. // max 标识缓存容量的最大值, = 0 标识无限缓存
  19. max int
  20.  
  21. // list 是 entry 循环双向链表
  22. list *list.List
  23.  
  24. // pond entry 缓存池子 key -> entry
  25. pond map[interface{}]*list.Element
  26. }
  27.  
  28. // New 构建 LRU Cache 缓存结构
  29. func New(max int) *Cache {
  30. return &Cache{
  31. max: max,
  32. list: list.New(),
  33. pond: make(map[interface{}]*list.Element),
  34. }
  35. }
  36.  
  37. func (c *Cache) delete(key interface{}) {
  38. element, ok := c.pond[key]
  39. if ok {
  40. delete(c.pond, key)
  41. c.list.Remove(element)
  42. return
  43. }
  44. }
  45.  
  46. // Set 设置缓存
  47. func (c *Cache) Set(key, val interface{}) {
  48. c.rw.Lock()
  49. defer c.rw.Unlock()
  50.  
  51. // 看是否进入删除分支
  52. if val == nil {
  53. c.delete(key)
  54. return
  55. }
  56.  
  57. element, ok := c.pond[key]
  58. if ok {
  59. // 重新设置 value 数据
  60. element.Value.(*entry).val = val
  61. // set key nil exists 进入 update 逻辑
  62. c.list.MoveToFront(element)
  63. return
  64. }
  65.  
  66. // 首次添加
  67. c.pond[key] = c.list.PushFront(&entry{key, val})
  68.  
  69. // 数据过多, 删除尾巴数据
  70. if c.list.Len() > c.max && c.max > 0 {
  71. delete(c.pond, c.list.Remove(c.list.Back()).(*entry).key)
  72. }
  73. }
  74.  
  75. // Get 获取缓存
  76. func (c *Cache) Get(key interface{}) (val interface{}, ok bool) {
  77. c.rw.RLock()
  78. defer c.rw.RUnlock()
  79.  
  80. if element, ok := c.pond[key]; ok {
  81. // 获取指定缓存值
  82. val, ok = element.Value.(*entry).val, true
  83. // 调整缓存热点
  84. c.list.MoveToFront(element)
  85. }
  86. return
  87. }

原理是 1. RWLock 做线程安全 2. list 双向链表保存时间新老关系 2. map 为了让时间复杂度到 O(1).

使用教程:

  1. // 创建
  2. c := cache.New(1)
  3.  
  4. // 设置
  5. c.Set("123", "123")
  6. c.Set("234", "234")
  7.  
  8. // 使用
  9. fmt.Println(c.Get("123"))
  10. fmt.Println(c.Get("234"))
  11.  
  12. // 删除
  13. c.Set("123", nil)

2. container/list.go

2.1 list 数据结构

上面 LRU Cache 代码中引用了 "container/list" , 简单分析下 list, 加深基础数据结构的了解.

  1. // Copyright 2009 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4.  
  5. // Package list implements a doubly linked list.
  6. //
  7. // To iterate over a list (where l is a *List):
  8. // for e := l.Front(); e != nil; e = e.Next() {
  9. // // do something with e.Value
  10. // }
  11. //
  12. package list
  13.  
  14. // Element is an element of a linked list.
  15. type Element struct {
  16. // Next and previous pointers in the doubly-linked list of elements.
  17. // To simplify the implementation, internally a list l is implemented
  18. // as a ring, such that &l.root is both the next element of the last
  19. // list element (l.Back()) and the previous element of the first list
  20. // element (l.Front()).
  21. next, prev *Element
  22.  
  23. // The list to which this element belongs.
  24. list *List
  25.  
  26. // The value stored with this element.
  27. Value interface{}
  28. }
  29.  
  30. // Next returns the next list element or nil.
  31. func (e *Element) Next() *Element {
  32. if p := e.next; e.list != nil && p != &e.list.root {
  33. return p
  34. }
  35. return nil
  36. }
  37.  
  38. // Prev returns the previous list element or nil.
  39. func (e *Element) Prev() *Element {
  40. if p := e.prev; e.list != nil && p != &e.list.root {
  41. return p
  42. }
  43. return nil
  44. }
  45.  
  46. // List represents a doubly linked list.
  47. // The zero value for List is an empty list ready to use.
  48. type List struct {
  49. root Element // sentinel list element, only &root, root.prev, and root.next are used
  50. len int // current list length excluding (this) sentinel element
  51. }

它是个特殊循环双向链表数据结构, 特殊之处在于 Element::List  指向头结点(root list).

关于业务 list.go 具体实现部分我们不表.

2.2 list 使用例子

  1. func Test_List_Demo(t *testing.T) {
  2.  
  3. // Persion 普通人
  4. type Persion struct {
  5. Name string
  6. Age int
  7. }
  8.  
  9. pers := list.New()
  10.  
  11. // 链表数据填充
  12. pers.PushBack(&Persion{Name: "wang", Age: 31})
  13. pers.PushFront(&Persion{Name: "zhi", Age: 31})
  14.  
  15. fmt.Printf("List Len() = %d\n", pers.Len())
  16. if pers.Len() != 2 {
  17. t.Fatal("pers.Len() != 2 data faild")
  18. }
  19.  
  20. // 开始遍历数据
  21. for element := pers.Front(); element != nil; element = element.Next() {
  22. per, ok := element.Value.(*Persion)
  23. if !ok {
  24. panic(fmt.Sprint("Persion list faild", element.Value))
  25. }
  26. fmt.Println(per)
  27. }
  28.  
  29. // 数据删除
  30. for element := pers.Front(); element != nil; {
  31. next := element.Next()
  32. pers.Remove(element)
  33. element = next
  34. }
  35.  
  36. fmt.Printf("List Len() = %d\n", pers.Len())
  37. if pers.Len() != 0 {
  38. t.Fatal("pers.Len() != 0 data faild")
  39. }
  40. }

单元测试结果:

  1. Running tool: /usr/local/go/bin/go test -timeout 30s -run ^Test_List_Demo$ demo/src/container/list -v -count=1
  2.  
  3. === RUN Test_List_Demo
  4. List Len() = 2
  5. &{zhi 31}
  6. &{wang 31}
  7. List Len() = 0
  8. --- PASS: Test_List_Demo (0.00s)
  9. PASS
  10. ok demo/src/container/list 0.002s

3. transport.go connLRU

抛一段 Go 源码中一处应用, 小学以小用

  1. //
  2. // src/net/http/transport.go
  3. //
  4.  
  5. // persistConn wraps a connection, usually a persistent one
  6. // (but may be used for non-keep-alive requests as well)
  7. type persistConn struct {
  8.   ...
      ..
      .
  9. }
  10.  
  11. type connLRU struct {
  12. ll *list.List // list.Element.Value type is of *persistConn
  13. m map[*persistConn]*list.Element
  14. }
  15.  
  16. // add adds pc to the head of the linked list.
  17. func (cl *connLRU) add(pc *persistConn) {
  18. if cl.ll == nil {
  19. cl.ll = list.New()
  20. cl.m = make(map[*persistConn]*list.Element)
  21. }
  22. ele := cl.ll.PushFront(pc)
  23. if _, ok := cl.m[pc]; ok {
  24. panic("persistConn was already in LRU")
  25. }
  26. cl.m[pc] = ele
  27. }
  28.  
  29. func (cl *connLRU) removeOldest() *persistConn {
  30. ele := cl.ll.Back()
  31. pc := ele.Value.(*persistConn)
  32. cl.ll.Remove(ele)
  33. delete(cl.m, pc)
  34. return pc
  35. }
  36.  
  37. // remove removes pc from cl.
  38. func (cl *connLRU) remove(pc *persistConn) {
  39. if ele, ok := cl.m[pc]; ok {
  40. cl.ll.Remove(ele)
  41. delete(cl.m, pc)
  42. }
  43. }
  44.  
  45. // len returns the number of items in the cache.
  46. func (cl *connLRU) len() int {
  47. return len(cl.m)
  48. }

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. DMS是临时解决方案?

    DMS是临时解决方案? Who Says DMS Is an Interim Solution? 现在是认真对待DMS驱动程序监控系统的时候了. 特斯拉(Tesla)在台湾高速公路上撞上翻倒卡车的镜头 ...

  2. GPU端到端目标检测YOLOV3全过程(下)

    GPU端到端目标检测YOLOV3全过程(下) Ubuntu18.04系统下最新版GPU环境配置 安装显卡驱动 安装Cuda 10.0 安装cuDNN 1.安装显卡驱动 (1)这里采用的是PPA源的安装 ...

  3. C语言代码区错误以及编译过程

    C语言代码区错误 欲想了解C语言代码段会有如何错误,我们必须首先了解编译器是如何把C语言文本信息编译成为可以执行的机器码的. 背景介绍 测试使用的C语言代码 导入标准库,定义宏变量,定义结构体,重命名 ...

  4. Java8 Lambda表达式、Optional类浅析

    1.概念 Lambda是一个匿名函数,可以将其理解为一段可以传递的代码(将代码像数据一样进行传递)可以写出更简洁.更灵活的代码.作为一种更紧凑的代码风格,使得java语言的表达能利得到了提升. 2. ...

  5. 【VBA】日期时间

    当前日期: Sub 测试() Debug.Print Date End Sub 当前时间: Sub 测试() Debug.Print Date End Sub 几月: Sub 测试() Debug.P ...

  6. 【SQLite】教程05-SQLite创建数据库、附加、分离数据库

    创建数据库 .quit命令 退出sqlite 提示符 .quit .dump 命令 使用 SQLite .dump 点命令来导出完整的数据库在一个文本文件中,如下所示: sqlite3 Test.db ...

  7. 【题解】codeforces 8c Looking for Order 状压dp

    题目描述 Lena喜欢秩序井然的生活.一天,她要去上大学了.突然,她发现整个房间乱糟糟的--她的手提包里的物品都散落在了地上.她想把所有的物品都放回她的手提包.但是,这里有一点问题:她一次最多只能拿两 ...

  8. Vue(5)计算属性computed

    前言 一般情况下属性都是放到data中的,但是有些属性可能是需要经过一些逻辑计算后才能得出来,那么我们可以把这类属性变成计算属性.比如以下: <div id="example" ...

  9. 【LeetCode每日一题 Day 5】5. 最长回文子串

    大家好,我是编程熊,今天是LeetCode每日一题的第五天,一起学习LeetCode第五题<最长回文子串>. 题意 给你一个字符串 s,找到 s 中最长的回文子串. 示例 输入:s = & ...

  10. 02 jumpserver系统设置

    2.系统设置: (1)基本设置: (2)邮件设置: 1)163邮箱设置: 2)在jumpserver上填写邮箱信息: 3)邮件测试信息如下: (3)邮件内容设置: (4)终端设置: (5)安全设置: