《Go源码解读篇》之常见数据结构(list)

原本打算用Go实现Java中常见的集合,简单实现ArrayList后,之后翻官网的package,发现了container/list,发现其实现十分的简洁,所以学习记录如下:

List实现准备工作

如果想实现一个list,首先想到解决问题:

  • 数据类型怎么处理?
  • Go中有没有像Java中Object似的,万能的数据类型呢?

多亏Go中存在interface{}这样万能的数据类型,问题就迎刃而解啦!

来看看我的ArrayList实现设计:

type ArrayList struct {
    size int32
    data []interface{}
}

我利用slice来实现简单的list(单向链表)操作,再看看官网的实现:

type Element struct {
    prev, next *Element
    list       *List
    Value      interface{}
}

type List struct {
    root Element
    len  int
}

哈哈,这么一对比,我都有些害羞啦!官网上更面向对象化,把List中元素抽象成了ElementElement并存在自己读取前后节点的方法。

Element中获取操作

  • 获取自身的Value
  • 获取前驱节点
  • 获取后继节点

Go的特点之一就是,其访问权限用首字母大小写来区分,Element可以直接获取其Value,而前后节点则分别提供了方法:

func (e *Element) Next() *Element {
    if p := e.next; e.list != nil && p != &e.list.root {
        return p
    }
    return nil
}

func (e *Element) Prev() *Element {
    if p := e.prev; e.list != nil && p != &e.list.root {
        return p
    }
    return nil
}

好不好奇,为什么类型是*Element? 当然是修改其值啦!
指针传递对象的引用,而非指针则是对对象的copy,指针使用规则如下:

  • 只要需要修改对象的时候,才必须使用指针,它不是Go语言的约束,而是一种自然约束。
  • 有时对象很小,用指针传递并不划算

List的初始化

List通过调用New()方法来初始化一个list,New()方法实现如下代码,Init()中将root.next,root.prev全都指向了root,这将在为下面的实现做铺垫

//Init initializes or clears list l
func (l *List) Init() *List {
    l.root.next = &l.root // next ---> root
    l.root.prev = &l.root // next ---> root
    //l.root.list = l //
    l.len = 0
    return l
}

func New() *List {
    //new 申请内存初始化
    list := new(List)
    return list.Init()
}

执行完上New()后,会产生一个类似{{0x000001, 0x000001, nil, nil}, 0}对象(0x000001只是个栗子),然而这个地址就是Element.list,保证list中的一致性。

List中的存储操作

List中的存储操作方法如下:

如上这些公开的方法都是建立在insert(e, at *Element)和insertValue(v interface{}, at *Element)方法之上的,代码如下:

//insert e after at
func (l *List) insert(e, at *Element) *Element {
    n := at.next
    at.next = e
    e.prev = at
    e.next = n
    n.prev = e
    e.list = l
    l.len++
    fmt.Println("l.root.prev:", l.root.prev, "  l.root.next:", l.root.next)
    return e

}

func (l *List) insertValue(v interface{}, at *Element) *Element {
    //创建新的节点
    return l.insert(&Element{Value: v}, at)
}

需要说明的是,insert(e, at *Element)实现是将e放到at后面啦,这对理解后面的代码很有帮助。
附上PushBack(v interface{})流程图:

pushback.png

由于PushFront(v interface{})执行过程与PushBack(v interface{})相反,所以附上其图如下,仔细观察,便知道其区别:

pushfront.png

假设上面Element的地址分别为0x001,0x002,0x003,分别将2,3放入列表中.
如上方法实现很简单,都在建立"insertValue(v interface{}, at *Element)"之上的,所以不在文章中贴代码啦!

List中的获取操作

一开始初始化的节点,就是存放头结点和尾节点指针用的,所以Back()操作返回其l.root.prev,Front()操作返回其l.root.next就搞定啦!

List中的删除操作

然而上述方法内部调用了remove(e *Element), 代码如下:

func (l *List) remove(e *Element) *Element {
    e.prev.next = e.next
    e.next.prev = e.prev
    //avoid memory leaks
    e.prev = nil
    e.next = nil
    e.list = nil
    l.len--
    return e
}

该方法的作用就是修改需要删除节点的前驱和后继节点,最后将其前后节点指针置空,防止内存异常。

List中的移动操作

所以移动操作,都是借助insertValue(v interface{}, e *Element)remove(e *Element)方法搞定的,使用的真是巧妙,具体查看源码吧!

注意:list不是协程安全的。

总体来说,通过翻阅list源码,掌握了它代码实现的list存储结构,更体会到了OOD的思想!(这篇文章,主要就是上面的两张图)

参考文献

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 4,253评论 0 16
  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,719评论 0 33
  • 今天,店里有事!没有在家等你放学!给你门上留了纸条!回家已经六点半多了!你说作业写完了!帮我收拾下家务吧?我说,行...
    许潇瑜妈妈阅读 159评论 0 0
  • 我的一天被分成很多片段 一只无名的狗 黑色的 背对着我 默默地死去了 我的一天被分成很多片段 记起桃肉在口中的酸甜...
    枝楼阅读 381评论 2 0
  • 今天是小雨来北京的第50天,工作还是没头绪。她本来以为一个海归硕士,在北京这么大的土地上总会有她的机会,可没想到她...
    微雨北北阅读 406评论 0 2