经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C 语言 » 查看文章
C 数据结构堆
来源:cnblogs  作者:喜欢兰花山丘  时间:2018/12/7 9:31:31  对本文有异议

引言 - 数据结构堆

  堆结构都很耳熟, 从堆排序优先级队列, 我们总会看见它的身影. 相关的资料太多了,

- https://zh.wikipedia.org/wiki/%E5%A0%86%E7%A9%8D

无数漂亮的图片接二连三, 但目前没搜到一个工程中可以舒服用的代码库. 本文由此痛点而来.

写一篇奇妙数据结构堆的终结代码. 耳熟终究比不过手热 ->---

对于 heap 接口思考, 我是这样设计

  1. #ifndef _H_HEAP
  2. #define _H_HEAP
  3.  
  4. //
  5. // cmp_f - 比较行为 > 0 or = 0 or < 0
  6. // : int add_cmp(const void * now, const void * node)
  7. //
  8. typedef int (* cmp_f)();
  9. //
  10. // node_f - 销毁行为
  11. // : void list_die(void * node)
  12. //
  13. typedef void (* node_f)(void * node);
  14. //
  15. // head_t 堆的类型结构
  16. //
  17. typedef struct heap * heap_t;
  18. //
  19. // heap_create - 创建符合规则的堆
  20. // fcmp : 比较行为, 规则 fcmp() <= 0
  21. // return : 返回创建好的堆对象
  22. //
  23. extern heap_t heap_create(cmp_f fcmp);
  24. //
  25. // heap_delete - 销毁堆
  26. // h : 堆对象
  27. // fdie : 销毁行为, 默认 NULL
  28. // return : void
  29. //
  30. extern void heap_delete(heap_t h, node_f fdie);
  31. //
  32. // heap_insert - 堆插入数据
  33. // h : 堆对象
  34. // node : 操作对象
  35. // return : void
  36. //
  37. extern void heap_insert(heap_t h, void * node);
  38. //
  39. // heap_remove - 堆删除数据
  40. // h : 堆对象
  41. // arg : 操作参数
  42. // fcmp : 比较行为, 规则 fcmp() == 0
  43. // return : 找到的堆节点
  44. //
  45. extern void * heap_remove(heap_t h, void * arg, cmp_f fcmp);
  46. //
  47. // heap_top - 查看堆顶结点数据
  48. // h : 堆对象
  49. // return : 堆顶节点
  50. //
  51. extern void * heap_top(heap_t h);
  52. //
  53. // heap_top - 摘掉堆顶结点数据
  54. // h : 堆对象
  55. // return : 返回堆顶节点
  56. //
  57. extern void * heap_pop(heap_t h);
  58. #endif//_H_HEAP

heap_t 是不完全类型实体指针, 其中 struct heap 是这样设计

  1. #include "heap.h"
  2. #include <stdlib.h>
  3. #include <assert.h>
  4.  
  5. #define UINT_HEAP (1<<5u)
  6.  
  7. struct heap {
  8. cmp_f fcmp; // 比较行为
  9. unsigned len; // heap 长度
  10. unsigned cap; // heap 容量
  11. void ** data; // 数据节点数组
  12. };
  13. // heap_expand - 添加节点扩容
  14. inline void heap_expand(struct heap * h) {
  15. if (h->len >= h->cap) {
  16. h->data = realloc(h->data, h->cap<<=1);
  17. assert(h->data);
  18. }
  19. }

从中可以看出当前堆结构是可以保存 void * 数据. 其中通过 heap::fcmp 比较行为来调整关系.

有了堆的数据结构定义, 那么堆的创建和销毁业务代码就被无脑的确定了 ~

  1. //
  2. // heap_create - 创建符合规则的堆
  3. // fcmp : 比较行为, 规则 fcmp() <= 0
  4. // return : 返回创建好的堆对象
  5. //
  6. inline heap_t
  7. heap_create(cmp_f fcmp) {
  8. struct heap * h = malloc(sizeof(struct heap));
  9. assert(h && fcmp);
  10. h->fcmp = fcmp;
  11. h->len = 0;
  12. h->cap = UINT_HEAP;
  13. h->data = malloc(sizeof(void *) * UINT_HEAP);
  14. assert(h->data && UINT_HEAP > 0);
  15. return h;
  16. }
  17. //
  18. // heap_delete - 销毁堆
  19. // h : 堆对象
  20. // fdie : 销毁行为, 默认 NULL
  21. // return : void
  22. //
  23. void
  24. heap_delete(heap_t h, node_f fdie) {
  25. if (NULL == h || h->data == NULL) return;
  26. if (fdie && h->len > 0)
  27. for (unsigned i = 0; i < h->len; ++i)
  28. fdie(h->data[i]);
  29. free(h->data);
  30. h->data = NULL;
  31. h->len = 0;
  32. free(h);
  33. }

随后将迎接这个终结者堆的全貌. 此刻读者可以先喝口水 : )

 

前言 - 写一段终结代码

  堆结构中最核心两处就是向下调整向上调整过程代码

  1. // down - 堆节点下沉, 从上到下沉一遍
  2. static void down(cmp_f fcmp, void * data[], unsigned len, unsigned x) {
  3. void * m = data[x];
  4. for (unsigned i = x * 2 + 1; i < len; i = x * 2 + 1) {
  5. if (i + 1 < len && fcmp(data[i+1], data[i]) < 0)
  6. ++i;
  7. if (fcmp(m, data[i]) <= 0)
  8. break;
  9. data[x] = data[i];
  10. x = i;
  11. }
  12. data[x] = m;
  13. }
  14. // up - 堆节点上浮, 从下到上浮一遍
  15. static void up(cmp_f fcmp, void * node, void * data[], unsigned x) {
  16. while (x > 0) {
  17. void * m = data[(x-1)>>1];
  18. if (fcmp(m, node) <= 0)
  19. break;
  20. data[x] = m;
  21. x = (x-1)>>1;
  22. }
  23. data[x] = node;
  24. }

如何理解其中奥妙呢. 可以这么看, 索引 i 节点的左子树索引为 2i+1, 右子树树索引为 2i+2 = (2i+1)+1.

相反的索引为 i 节点的父亲节点就是 (i-1)/2 = (i-1)>>1. 这就是堆节点调整的无上奥妙.  随后的代码就

很轻松出手了

  1. //
  2. // heap_insert - 堆插入数据
  3. // h : 堆对象
  4. // node : 操作对象
  5. // return : void
  6. //
  7. inline void
  8. heap_insert(heap_t h, void * node) {
  9. heap_expand(h);
  10. up(h->fcmp, node, h->data, h->len++);
  11. }
  12. //
  13. // heap_top - 查看堆顶结点数据
  14. // h : 堆对象
  15. // return : 堆顶节点
  16. //
  17. inline void *
  18. heap_top(heap_t h) {
  19. return h->len <= 0 ? NULL : *h->data;
  20. }
  21. //
  22. // heap_top - 摘掉堆顶结点数据
  23. // h : 堆对象
  24. // return : 返回堆顶节点
  25. //
  26. inline void *
  27. heap_pop(heap_t h) {
  28. void * node = heap_top(h);
  29. if (node && --h->len > 0) {
  30. // 尾巴节点一定比小堆顶节点大, 那么要下沉
  31. h->data[0] = h->data[h->len];
  32. down(h->fcmp, h->data, h->len, 0);
  33. }
  34. return node;
  35. }

看完上面代码可以再回看下 down 和 up 代码布局. 是不是堆节点调整全部技巧已经了然于胸 ~

随后我们介绍堆删除任意节点大致算法思路

  1' 循环遍历, 找到要删除节点

  2' 如果删除后堆空, 或者删除的是最后节点, 那直接搞定

  3' 最后节点复制到待删除节点位置处

  4' 最后节点和待删除节点权值相等, 不调整节点关系

  5' 最后节点比待删除节点权值大, 向下调整节点关系(基于小顶堆设计)

  6' 最后节点比待删除节点权值小, 向上调整节点关系

从上可以看出堆删除节点算法复杂度是 O(n) + O(logn) = O(n). 请欣赏具体代码

  1. //
  2. // heap_remove - 堆删除数据
  3. // h : 堆对象
  4. // arg : 操作参数
  5. // fcmp : 比较行为, 规则 fcmp() == 0
  6. // return : 找到的堆节点
  7. //
  8. void *
  9. heap_remove(heap_t h, void * arg, cmp_f fcmp) {
  10. if (h == NULL || h->len <= 0)
  11. return NULL;
  12. // 开始查找这个节点
  13. unsigned i = 0;
  14. fcmp = fcmp ? fcmp : h->fcmp;
  15. do {
  16. void * node = h->data[i];
  17. if (fcmp(arg, node) == 0) {
  18. if (--h->len > 0 && h->len != i) {
  19. // 尾巴节点和待删除节点比较
  20. int ret = h->fcmp(h->data[h->len], node);
  21. // 小顶堆, 新的值比老的值小, 那么上浮
  22. if (ret < 0)
  23. up(h->fcmp, h->data[h->len], h->data, i);
  24. else if (ret > 0) {
  25. // 小顶堆, 新的值比老的值大, 那么下沉
  26. h->data[i] = h->data[h->len];
  27. down(h->fcmp, h->data, h->len, i);
  28. }
  29. }
  30. return node;
  31. }
  32. } while (++i < h->len);
  33. return NULL;
  34. }

到这堆数据结构基本代码都已经搞定. 开始写写测试用例跑跑

  1. #include "heap.h"
  2. #include <stdio.h>
  3.  
  4. struct node {
  5. int value;
  6. };
  7. static inline int node_cmp(const struct node * l, const struct node * r) {
  8. return l->value - r->value;
  9. }
  10. static void heap_print(heap_t h) {
  11. struct heap {
  12. cmp_f fcmp; // 比较行为
  13. unsigned len; // heap 长度
  14. unsigned cap; // heap 容量
  15. void ** data; // 数据节点数组
  16. } * x = (struct heap *)h;
  17. // 数据打印for (unsigned i = 0; i < x->len; ++i) {
  18. struct node * node = x->data[i];
  19. printf("%d ", node->value);
  20. }
  21. putchar('\n');
  22. }
  23. int main() {
  24. heap_t h = heap_create(node_cmp);
  25. struct node a[] = { { 53 }, { 17 }, { 78 }, { 9 }, { 45 }, { 65 }, { 87 }, { 23} };
  26. for (int i = 0; i < sizeof a / sizeof *a; ++i)
  27. heap_insert(h, a + i);
  28. heap_print(h);
  29. // 数据打印
  30. struct node * node;
  31. while ((node = heap_pop(h))) {
  32. printf("%d ", node->value);
  33. }
  34. putchar('\n');
  35. // 重新插入数据
  36. for (int i = 0; i < sizeof a / sizeof *a; ++i)
  37. heap_insert(h, a + i);
  38. // 删除操作 - 下沉
  39. heap_remove(h, &(struct node){ 17 }, NULL);
  40. heap_print(h);
  41. // 插入操作
  42. heap_insert(h, &(struct node){ 17 });
  43. heap_print(h);
  44. // 删除操作 - 上浮
  45. heap_remove(h, &(struct node){ 78 }, NULL);
  46. heap_print(h);
  47. heap_delete(h, NULL);
  48. return 0;
  49. }

最终运行结果如下

 

后续堆相关代码变化, 可以参照  heap - https://github.com/wangzhione/structc/blob/master/structc/struct/heap.c

说到引用 github 想起一个 git 好用配置安利给大家 ~ 从此 git ll 就活了.

  1. git config --global color.diff auto
    git config --global color.status auto
  2. git config --global color.branch auto
  3. git config --global color.interactive auto
  4. git config --global alias.ll "log --graph --all --pretty=format:'%Cred%h %Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"

奇妙数据结构堆, 终结在这里, 后面内容可以忽略. 期待下次再扯了 ~

 

正文 - 顺带赠送个点心

  其实到这本不该再说什么. 单纯看上面就足够了. 但不知道有没有朋友觉得你总是说 C 数据结构. 效

果好吗? 对技术提升效果明显吗? 这里不妨利用我们对 C 理解, 来分析一个业务代码. 感受下一通百通.

我试着用 Go 中数据结构源码举例子. 重点看下 Go 源码包中 "container/list" 链表用法(比较简单)

  1. package main
  2.  
  3. import (
  4. "container/list"
  5. "fmt"
  6. )
  7.  
  8. func main() {
  9. // 构造链表对象
  10. pers := list.New()
  11.  
  12. // Persion 普通人对象
  13. type Persion struct {
  14. Name string
  15. Age int
  16. }
  17.  
  18. // 链表对象数据填充
  19. pers.PushBack(&Persion{"wang", 27})
  20. pers.PushFront(&Persion{"zhi", 27})
  21.  
  22. // 开始遍历处理
  23. for e := pers.Front(); e != nil; e = e.Next() {
  24. per, ok := e.Value.(*Persion)
  25. if !ok {
  26. panic(fmt.Sprint("Persion List faild", e.Value))
  27. }
  28. fmt.Println(per)
  29. }
  30.  
  31. for e := pers.Front(); e != nil; {
  32. next := e.Next()
  33. pers.Remove(e)
  34. e = next
  35. }
  36. fmt.Println(pers.Len())
  37. }

运行结果是

  1. $ go run list-demo.go
  2. &{zhi 27}
  3. &{wang 27}
  4. 0

通过上面演示 Demo, 大致知道了 list 包用法. 随后开始着手解析 "container/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. }
  52.  
  53. // Init initializes or clears list l.
  54. func (l *List) Init() *List {
  55. l.root.next = &l.root
  56. l.root.prev = &l.root
  57. l.len = 0
  58. return l
  59. }
  60.  
  61. // New returns an initialized list.
  62. func New() *List { return new(List).Init() }
  63.  
  64. // Len returns the number of elements of list l.
  65. // The complexity is O(1).
  66. func (l *List) Len() int { return l.len }
  67.  
  68. // Front returns the first element of list l or nil if the list is empty.
  69. func (l *List) Front() *Element {
  70. if l.len == 0 {
  71. return nil
  72. }
  73. return l.root.next
  74. }
  75.  
  76. // Back returns the last element of list l or nil if the list is empty.
  77. func (l *List) Back() *Element {
  78. if l.len == 0 {
  79. return nil
  80. }
  81. return l.root.prev
  82. }
  83.  
  84. // lazyInit lazily initializes a zero List value.
  85. func (l *List) lazyInit() {
  86. if l.root.next == nil {
  87. l.Init()
  88. }
  89. }
  90.  
  91. // insert inserts e after at, increments l.len, and returns e.
  92. func (l *List) insert(e, at *Element) *Element {
  93. n := at.next
  94. at.next = e
  95. e.prev = at
  96. e.next = n
  97. n.prev = e
  98. e.list = l
  99. l.len++
  100. return e
  101. }
  102.  
  103. // insertValue is a convenience wrapper for insert(&Element{Value: v}, at).
  104. func (l *List) insertValue(v interface{}, at *Element) *Element {
  105. return l.insert(&Element{Value: v}, at)
  106. }
  107.  
  108. // remove removes e from its list, decrements l.len, and returns e.
  109. func (l *List) remove(e *Element) *Element {
  110. e.prev.next = e.next
  111. e.next.prev = e.prev
  112. e.next = nil // avoid memory leaks
  113. e.prev = nil // avoid memory leaks
  114. e.list = nil
  115. l.len--
  116. return e
  117. }
  118.  
  119. // Remove removes e from l if e is an element of list l.
  120. // It returns the element value e.Value.
  121. // The element must not be nil.
  122. func (l *List) Remove(e *Element) interface{} {
  123. if e.list == l {
  124. // if e.list == l, l must have been initialized when e was inserted
  125. // in l or l == nil (e is a zero Element) and l.remove will crash
  126. l.remove(e)
  127. }
  128. return e.Value
  129. }
  130.  
  131. // PushFront inserts a new element e with value v at the front of list l and returns e.
  132. func (l *List) PushFront(v interface{}) *Element {
  133. l.lazyInit()
  134. return l.insertValue(v, &l.root)
  135. }
  136.  
  137. // PushBack inserts a new element e with value v at the back of list l and returns e.
  138. func (l *List) PushBack(v interface{}) *Element {
  139. l.lazyInit()
  140. return l.insertValue(v, l.root.prev)
  141. }
  142.  
  143. // InsertBefore inserts a new element e with value v immediately before mark and returns e.
  144. // If mark is not an element of l, the list is not modified.
  145. // The mark must not be nil.
  146. func (l *List) InsertBefore(v interface{}, mark *Element) *Element {
  147. if mark.list != l {
  148. return nil
  149. }
  150. // see comment in List.Remove about initialization of l
  151. return l.insertValue(v, mark.prev)
  152. }
  153.  
  154. // InsertAfter inserts a new element e with value v immediately after mark and returns e.
  155. // If mark is not an element of l, the list is not modified.
  156. // The mark must not be nil.
  157. func (l *List) InsertAfter(v interface{}, mark *Element) *Element {
  158. if mark.list != l {
  159. return nil
  160. }
  161. // see comment in List.Remove about initialization of l
  162. return l.insertValue(v, mark)
  163. }
  164.  
  165. // MoveToFront moves element e to the front of list l.
  166. // If e is not an element of l, the list is not modified.
  167. // The element must not be nil.
  168. func (l *List) MoveToFront(e *Element) {
  169. if e.list != l || l.root.next == e {
  170. return
  171. }
  172. // see comment in List.Remove about initialization of l
  173. l.insert(l.remove(e), &l.root)
  174. }
  175.  
  176. // MoveToBack moves element e to the back of list l.
  177. // If e is not an element of l, the list is not modified.
  178. // The element must not be nil.
  179. func (l *List) MoveToBack(e *Element) {
  180. if e.list != l || l.root.prev == e {
  181. return
  182. }
  183. // see comment in List.Remove about initialization of l
  184. l.insert(l.remove(e), l.root.prev)
  185. }
  186.  
  187. // MoveBefore moves element e to its new position before mark.
  188. // If e or mark is not an element of l, or e == mark, the list is not modified.
  189. // The element and mark must not be nil.
  190. func (l *List) MoveBefore(e, mark *Element) {
  191. if e.list != l || e == mark || mark.list != l {
  192. return
  193. }
  194. l.insert(l.remove(e), mark.prev)
  195. }
  196.  
  197. // MoveAfter moves element e to its new position after mark.
  198. // If e or mark is not an element of l, or e == mark, the list is not modified.
  199. // The element and mark must not be nil.
  200. func (l *List) MoveAfter(e, mark *Element) {
  201. if e.list != l || e == mark || mark.list != l {
  202. return
  203. }
  204. l.insert(l.remove(e), mark)
  205. }
  206.  
  207. // PushBackList inserts a copy of an other list at the back of list l.
  208. // The lists l and other may be the same. They must not be nil.
  209. func (l *List) PushBackList(other *List) {
  210. l.lazyInit()
  211. for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
  212. l.insertValue(e.Value, l.root.prev)
  213. }
  214. }
  215.  
  216. // PushFrontList inserts a copy of an other list at the front of list l.
  217. // The lists l and other may be the same. They must not be nil.
  218. func (l *List) PushFrontList(other *List) {
  219. l.lazyInit()
  220. for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
  221. l.insertValue(e.Value, &l.root)
  222. }
  223. }

list 包中最核心的数据结构无外乎 Element 和 List 互相引用的结构

  1. // Element is an element of a linked list.
  2. type Element struct {
  3. // Next and previous pointers in the doubly-linked list of elements.
  4. // To simplify the implementation, internally a list l is implemented
  5. // as a ring, such that &l.root is both the next element of the last
  6. // list element (l.Back()) and the previous element of the first list
  7. // element (l.Front()).
  8. next, prev *Element
  9.  
  10. // The list to which this element belongs.
  11. list *List
  12.  
  13. // The value stored with this element.
  14. Value interface{}
  15. }
  16.  
  17. // Next returns the next list element or nil.
  18. func (e *Element) Next() *Element {
  19. if p := e.next; e.list != nil && p != &e.list.root {
  20. return p
  21. }
  22. return nil
  23. }
  24.  
  25. // Prev returns the previous list element or nil.
  26. func (e *Element) Prev() *Element {
  27. if p := e.prev; e.list != nil && p != &e.list.root {
  28. return p
  29. }
  30. return nil
  31. }
  32.  
  33. // List represents a doubly linked list.
  34. // The zero value for List is an empty list ready to use.
  35. type List struct {
  36. root Element // sentinel list element, only &root, root.prev, and root.next are used
  37. len int // current list length excluding (this) sentinel element
  38. }

它是一个特殊循环双向链表. 特殊在 Element::list 指向头节点. 

随着我们对 list 内存布局理解后, 后面的业务代码实现起来就很一般了. 例如这里

  1. // PushBackList inserts a copy of an other list at the back of list l.
  2. // The lists l and other may be the same. They must not be nil.
  3. func (l *List) PushBackList(other *List) {
  4. l.lazyInit()
  5. for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
  6. l.insertValue(e.Value, l.root.prev)
  7. }
  8. }

其实可以实现的更贴合 list 库总体的风格, 性能还更好

  1. // PushBackList inserts a copy of an other list at the back of list l.
  2. // The lists l and other may be the same. They must not be nil.
  3. func (l *List) PushBackList(other *List) {
  4. l.lazyInit()
  5. for e := other.Front(); e != nil; e = e.Next() {
  6. l.insertValue(e.Value, l.root.prev)
  7. }
  8. }

是不是发现上层代码理解起来心智负担不大. 不过 go 中 slice list map 都不是线程安全的.

特殊场景需要自行加锁. 这里不过多扯. 以后有机会会详细分析 Go 中锁源码实现. 最后通过

上面 list 包真实现一个 LRU Cache

  1. package cache
  2.  
  3. import (
  4. "container/list"
  5. "sync"
  6. )
  7.  
  8. // entry 存储实体内容
  9. type entry struct {
  10. key interface{}
  11. value interface{}
  12. }
  13.  
  14. // Cache LRU 缓存实现
  15. type Cache struct {
  16. // x 保证 LRU 访问安全
  17. m sync.Mutex
  18.  
  19. // max 表示缓存容量的最大值, 0 表示无限缓存
  20. max uint
  21.  
  22. // list 循环双向链表
  23. list *list.List
  24.  
  25. // pond 缓存的池子
  26. pond map[interface{}]*list.Element
  27. }
  28.  
  29. // New 新建一个 LRU 缓存对象
  30. func New(max uint) *Cache {
  31. return &Cache{
  32. max: max,
  33. list: list.New(),
  34. pond: make(map[interface{}]*list.Element),
  35. }
  36. }
  37.  
  38. // remove 通过 *list.Element 删除
  39. func (c *Cache) remove(e *list.Element) {
  40. n, ok := c.list.Remove(e).(*entry)
  41. if ok {
  42. delete(c.pond, n.key)
  43. }
  44. }
  45.  
  46. // Set 添加缓存
  47. func (c *Cache) Set(key, value interface{}) {
  48. c.m.Lock()
  49. defer c.m.Unlock()
  50.  
  51. if e, ok := c.pond[key]; ok {
  52. if value == nil {
  53. // Set key nil <=> Remove key
  54. c.remove(e)
  55. } else {
  56. e.Value = value
  57. c.list.MoveToFront(e)
  58. }
  59. return
  60. }
  61.  
  62. // 如果是首次添加
  63. c.pond[key] = c.list.PushFront(&entry{key, value})
  64.  
  65. // 如果超出池子缓存开始清理
  66. if c.max != 0 && uint(c.list.Len()) > c.max {
  67. c.remove(c.list.Back())
  68. }
  69. }
  70.  
  71. // Get 获取缓存
  72. func (c *Cache) Get(key interface{}) (interface{}, bool) {
  73. c.m.Lock()
  74. defer c.m.Unlock()
  75.  
  76. if e, ok := c.pond[key]; ok {
  77. c.list.MoveToFront(e)
  78. return e.Value.(*entry).value, true
  79. }
  80. return nil, false
  81. }

用起来很容易

  1. c := cache.New(1)
  2. c.Set("123", "123")
  3. c.Set("234", "234")
  4. fmt.Println(c.Get("123"))
  5. fmt.Println(c.Get("234"))

是不是离开了 C, 整个世界也很简单. 没啥设计模式, 有的是性能还可以, 也能用.

希望能帮到有心人 ~

 

后记 - 那个打开的大门

你曾是少年 - https://music.163.com/#/song?id=426027293

每个男人心里都有一块净土, 只不过生活所逼硬生生的, 藏在心底最深处 . ... ..

 

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号