golang 提供了几个简单的容器供我们使用,本文在介绍几种Golang 容器的基础上,实现一个基于Golang 容器的LRU算法。

容器介绍

Golang 容器位于 container 包下,提供了三种包供我们使用,heap、list、ring. 下面我们分别学习。

heap

heap 是一个堆的实现。一个堆正常保证了获取/弹出最大(最小)元素的时间为log n、插入元素的时间为log n.

golang的堆实现接口如下:

// src/container/heap.go
type Interface interface {
sort.Interface
Push(x interface{}) // add x as element Len()
Pop() interface{} // remove and return element Len() - 1.
}

heap 是基于 sort.Interface 实现的。

// src/sort/
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}

因此,如果要使用官方提供的heap,需要我们实现如下几个接口

Len() int {}              // 获取元素个数
Less(i, j int) bool {} // 比较方法
Swap(i, j int) // 元素交换方法
Push(x interface{}){} // 在末尾追加元素
Pop() interface{} // 返回末尾元素

然后在使用时,我们可以使用如下几种方法

// 初始化一个堆
func Init(h Interface){}
// push一个元素倒堆中
func Push(h Interface, x interface{}){}
// pop 堆顶元素
func Pop(h Interface) interface{} {}
// 删除堆中某个元素,时间复杂度 log n
func Remove(h Interface, i int) interface{} {}
// 调整i位置的元素位置(位置I的数据变更后)
func Fix(h Interface, i int){}

list 链表

list 实现了一个双向链表,链表不需要实现heap 类似的接口,可以直接使用。

链表的构造:

  // 返回一个链表对象
func New() *List {}

官方提供了丰富的方法供我们操作列表,方法如下:

  // 返回链表的长度
func (l *List) Len() int {}
// 返回链表中的第一个元素
func (l *List) Front() *Element {}
// 返回链表中的末尾元素
func (l *List) Back() *Element {}
// 移除链表中的某个元素
func (l *List) Remove(e *Element) interface{} {}
// 在表头插入值为 v 的元素
func (l *List) PushFront(v interface{}) *Element {}
// 在表尾插入值为 v 的元素
func (l *List) PushBack(v interface{}) *Element {}
// 在mark之前插入值为v 的元素
func (l *List) InsertBefore(v interface{}, mark *Element) *Element {}
// 在mark 之后插入值为 v 的元素
func (l *List) InsertAfter(v interface{}, mark *Element) *Element {}
// 移动e某个元素到表头
func (l *List) MoveToFront(e *Element) {}
// 移动e到队尾
func (l *List) MoveToBack(e *Element) {}
// 移动e到mark之前
func (l *List) MoveBefore(e, mark *Element) {}
// 移动e 到mark 之后
func (l *List) MoveAfter(e, mark *Element) {}
// 追加到队尾
func (l *List) PushBackList(other *List) {}
// 将链表list放在队列前
func (l *List) PushFrontList(other *List) {}

我们可以通过 Value 方法访问 Element 中的元素。除此之外,我们还可以用下面方法做链表遍历:

// 返回下一个元素
func (e *Element) Next() *Element {}
// 返回上一个元素
func (e *Element) Prev() *Element {}

下面是队列的遍历的例子:

// l 为队列,
for e := l.Front(); e != nil; e = e.Next() {
//通过 e.Value 做数据访问
}

ring 循环列表

container 中的循环列表是采用链表实现的。

// 构造一个包含N个元素的循环列表
func New(n int) *Ring {}
// 返回列表下一个元素
func (r *Ring) Next() *Ring {}
// 返回列表上一个元素
func (r *Ring) Prev() *Ring {}
// 移动n个元素 (可以前移,可以后移)
func (r *Ring) Move(n int) *Ring {}
// 把 s 链接到 r 后面。如果s 和r 在一个ring 里面,会把r到s的元素从ring 中删掉
func (r *Ring) Link(s *Ring) *Ring {}
// 删除n个元素 (内部就是ring 移动n个元素,然后调用Link)
func (r *Ring) Unlink(n int) *Ring {}
// 返回Ring 的长度,时间复杂度 n
func (r *Ring) Len() int {}
// 遍历Ring,执行 f 方法 (不建议内部修改ring)
func (r *Ring) Do(f func(interface{})) {}

访问Ring 中元素,直接 Ring.Value 即可。

容器的使用

下面,我们通过map 和 官方包中的双向链表实现一个简单的lru 算法,用来熟悉golang 容器的使用。

LRU 算法 (Least Recently Used),在做缓存置换时用的比较多。逐步淘汰最近未使用的cache,而使我们的缓存中持续保持着最近使用的数据。

package main

import "fmt"
import "container/list" // lru 中的数据
type Node struct {
K, V interface{}
} // 链表 + map
type LRU struct {
list *list.List
cacheMap map[interface{}]*list.Element
Size int
} // 初始化一个LRU
func NewLRU(cap int) *LRU {
return &LRU{
Size: cap,
list: list.New(),
cacheMap: make(map[interface{}]*list.Element, cap),
}
} // 获取LRU中数据
func (lru *LRU) Get(k interface{}) (v interface{}, ret bool) {
// 如果存在,则把数据放到链表最前面
if ele, ok := lru.cacheMap[k]; ok {
lru.list.MoveToFront(ele)
return ele.Value.(*Node).V, true
} return nil, false
} // 设置LRU中数据
func (lru *LRU) Set(k, v interface{}) {
// 如果存在,则把数据放到最前面
if ele, ok := lru.cacheMap[k]; ok {
lru.list.MoveToFront(ele)
ele.Value.(*Node).V = v // 更新数据值
return
} // 如果数据是满的,先删除数据,后插入
if lru.list.Len() == lru.Size {
last := lru.list.Back()
node := last.Value.(*Node)
delete(lru.cacheMap, node.K)
lru.list.Remove(last)
} ele := lru.list.PushFront(&Node{K: k, V: v})
lru.cacheMap[k] = ele
}

其他

  1. 上述的容器都不是goroutines 安全的
  2. 上面的lr 也不是goroutines 安全的
  3. Ring 中不建议在Do 方法中修改Ring 的指针,行为是未定义的

golang 容器的学习与实践的更多相关文章

  1. Weex学习与实践

    Weex学习与实践(一):Weex,你需要知道的事 本文主要介绍包括Weex基本介绍.Weex源码结构.初始化工程.we代码结构.Weex的生命周期.Weex的工作原理.页面间通信.boxmodel ...

  2. hadoop2.5.2学习及实践笔记(二)—— 编译源代码及导入源码至eclipse

    生产环境中hadoop一般会选择64位版本,官方下载的hadoop安装包中的native库是32位的,因此运行64位版本时,需要自己编译64位的native库,并替换掉自带native库. 源码包下的 ...

  3. ansible 学习与实践

    title: ansible 学习与实践 date: 2016-05-06 16:17:28 tags: --- ansible 学习与实践 一 介绍 ansible是新出现的运维工具是基于Pytho ...

  4. Google App Engine 学习和实践

    这个周末玩了玩Google App Engine,随手写点东西,算是学习笔记吧.不当之处,请多多指正. 作者:liigo,2009/04/26夜,大连 原创链接:http://blog.csdn.ne ...

  5. PMBOK 学习与实践分享视频

    本系列为自己在学习PMBOK时进行的总结与分享,每一节主要包括两部分: 对PMBOK本身的一个结构笔记和讲解. 对自己项目管理工作的一个总结和思考. PMBOK 学习与实践分享视频内容清单 人力资源管 ...

  6. NLP+词法系列(二)︱中文分词技术简述、深度学习分词实践(CIPS2016、超多案例)

    摘录自:CIPS2016 中文信息处理报告<第一章 词法和句法分析研究进展.现状及趋势>P4 CIPS2016 中文信息处理报告下载链接:http://cips-upload.bj.bce ...

  7. Docker容器的原理与实践(上)

    本文来自网易云社区. 虚拟化 是一种资源管理技术,将计算机的各种资源予以抽象.转换后呈现出来, 打破实体结构间的不可切割的障碍,使用户可以比原本更好的方式来应用这些资源. Hypervisor 一种运 ...

  8. Docker容器的原理与实践 (下)

    欢迎访问网易云社区,了解更多网易技术产品运营经验. Docker原理分析 Docker架构 镜像原理 镜像是一个只读的容器模板,含有启动docker容器所需的文件系统结构及内容Docker以镜像和在镜 ...

  9. 免考final linux提权与渗透入门——Exploit-Exercise Nebula学习与实践

    免考final linux提权与渗透入门--Exploit-Exercise Nebula学习与实践 0x0 前言 Exploit-Exercise是一系列学习linux下渗透的虚拟环境,官网是htt ...

随机推荐

  1. 自己实现一个 DFA 串模式识别器

    自己实现一个 DFA 串模式识别器 前言 这是我编译原理课程的实验.希望读完这篇文章的人即便不知道 NFA,DFA 和正规表达式是什么,也能够对它们有一个简单的理解,并能自己去实现一个能够识别特定模式 ...

  2. sql server临时删除/禁用非聚集索引并重新创建加回/启用的简便编程方法研究对比

    前言: 由于新型冠状病毒影响,博主(zhang502219048)在2020年1月份从广东广州工作地回到广东揭阳产业转移工业园磐东街道(镇里有阳美亚洲玉都.五金之乡,素以“金玉”闻名)老家后,还没过去 ...

  3. X - Ehab and Path-etic MEXs CodeForces - 1325C

    MMP,差一点就做对了. 题目大意:给你一个树,对这个树的边进行编号,编号要求从0到n-1,不可重复,要求MEX(U,V)尽可能的小, MEX(x,y)的定义:从x到y的简单路径上,没有出现的最小编号 ...

  4. A - Oil Deposits DFS

    The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits. GeoSu ...

  5. Python - Python的基础知识结构,学习方法、难点和重点

    [原创]转载请注明作者Johnthegreat和本文链接. 相信大家都知道,Python很容易学,有编程基础的人,最多两个星期就可以很愉快的撸Python的代码了,那么具体涉及的知识有哪些,下面为大家 ...

  6. Python 七步捉虫法

    了解一些技巧助你减少代码查错时间. -- Maria Mckinley 在周五的下午三点钟(为什么是这个时间?因为事情总会在周五下午三点钟发生),你收到一条通知,客户发现你的软件出现一个错误.在有了初 ...

  7. LeetCode 面试题51. 数组中的逆序对

    面试题51. 数组中的逆序对 题目来源:https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/ 题目 在数组中的两个数字,如果 ...

  8. 一不小心实现了RPC

    前言 随着最近关注 cim 项目的人越发增多,导致提的问题以及 Bug 也在增加,在修复问题的过程中难免代码洁癖又上来了. 看着一两年前写的东西总是怀疑这真的是出自自己手里嘛?有些地方实在忍不住了便开 ...

  9. php utf-8

    header(”Content-Type: text/html; charset=UTF-8″) 控制器控释模板输出: 值: array (size=8) 1 => array (size=4) ...

  10. php静态变量的销毁

    什么都不说,先上代码: public function _childrenids($data,$cate_id,$clear=false) { static $arr = array(); if ($ ...