Go语言核心36讲
郝林
《Go并发编程实战》作者,前轻松筹大数据负责人
立即订阅
24139 人已学习
课程目录
已完结 54 讲
0/4登录后,你可以任选4讲全文学习。
开篇词+学习路线 (3讲)
开篇词 | 跟着学,你也能成为Go语言高手
免费
预习篇 | 写给0基础入门的Go语言学习者
50 | 学习专栏的正确姿势
模块一:Go语言基础知识 (6讲)
01 | 工作区和GOPATH
02 | 命令源码文件
03 | 库源码文件
04 | 程序实体的那些事儿(上)
05 | 程序实体的那些事儿(中)
06 | 程序实体的那些事儿 (下)
模块二:Go语言进阶技术 (16讲)
07 | 数组和切片
08 | container包中的那些容器
09 | 字典的操作和约束
10 | 通道的基本操作
11 | 通道的高级玩法
12 | 使用函数的正确姿势
13 | 结构体及其方法的使用法门
14 | 接口类型的合理运用
15 | 关于指针的有限操作
16 | go语句及其执行规则(上)
17 | go语句及其执行规则(下)
18 | if语句、for语句和switch语句
19 | 错误处理(上)
20 | 错误处理 (下)
21 | panic函数、recover函数以及defer语句 (上)
22 | panic函数、recover函数以及defer语句(下)
模块三:Go语言实战与应用 (27讲)
23 | 测试的基本规则和流程 (上)
24 | 测试的基本规则和流程(下)
25 | 更多的测试手法
26 | sync.Mutex与sync.RWMutex
27 | 条件变量sync.Cond (上)
28 | 条件变量sync.Cond (下)
29 | 原子操作(上)
30 | 原子操作(下)
31 | sync.WaitGroup和sync.Once
32 | context.Context类型
33 | 临时对象池sync.Pool
34 | 并发安全字典sync.Map (上)
35 | 并发安全字典sync.Map (下)
36 | unicode与字符编码
37 | strings包与字符串操作
38 | bytes包与字节串操作(上)
39 | bytes包与字节串操作(下)
40 | io包中的接口和工具 (上)
41 | io包中的接口和工具 (下)
42 | bufio包中的数据类型 (上)
43 | bufio包中的数据类型(下)
44 | 使用os包中的API (上)
45 | 使用os包中的API (下)
46 | 访问网络服务
47 | 基于HTTP协议的网络服务
48 | 程序性能分析基础(上)
49 | 程序性能分析基础(下)
尾声与思考题答案 (2讲)
尾声 | 愿你披荆斩棘,所向无敌
新年彩蛋 | 完整版思考题答案
Go语言核心36讲
登录|注册

08 | container包中的那些容器

郝林 2018-08-29
我们在上次讨论了数组和切片,当我们提到数组的时候,往往会想起链表。那么 Go 语言的链表是什么样的呢?
Go 语言的链表实现在标准库的container/list代码包中。这个代码包中有两个公开的程序实体——ListElement,List 实现了一个双向链表(以下简称链表),而 Element 则代表了链表中元素的结构。
那么,我今天的问题是:可以把自己生成的Element类型值传给链表吗?
我们在这里用到了List的四种方法。
MoveBefore方法和MoveAfter方法,它们分别用于把给定的元素移动到另一个元素的前面和后面。
MoveToFront方法和MoveToBack方法,分别用于把给定的元素移动到链表的最前端和最后端。
在这些方法中,“给定的元素”都是*Element类型的,*Element类型是Element类型的指针类型,*Element的值就是元素的指针。
func (l *List) MoveBefore(e, mark *Element)
func (l *List) MoveAfter(e, mark *Element)
func (l *List) MoveToFront(e *Element)
func (l *List) MoveToBack(e *Element)
具体问题是,如果我们自己生成这样的值,然后把它作为“给定的元素”传给链表的方法,那么会发生什么?链表会接受它吗?
这里,给出一个典型回答:不会接受,这些方法将不会对链表做出任何改动。因为我们自己生成的Element值并不在链表中,所以也就谈不上“在链表中移动元素”。更何况链表不允许我们把自己生成的Element值插入其中。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Go语言核心36讲》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(18)

  • 李皮皮皮皮皮
    1.list可以作为queue和
    stack的基础数据结构
    2.ring可以用来保存固定数量的元素,例如保存最近100条日志,用户最近10次操作
    3.heap可以用来排序。游戏编程中是一种高效的定时器实现方案
    2018-08-29
    59
  • 陌上人 .
    老师,之后的课可不可以多加一些图形解释,原理性的知识只用文字确实有些晦涩难懂
    2018-11-28
    1
    37
  • melon
    list的一个典型应用场景是构造FIFO队列;ring的一个典型应用场景是构造定长环回队列,比如网页上的轮播;heap的一个典型应用场景是构造优先级队列。
    2018-08-29
    17
  • louis
    郝老师,这里不太理解什么叫“自己生成的Element类型值”?把自己生成的Element类型值传给链表——这个能不能再通俗点描述?

    作者回复: 比如你用 list.New 函数创建了一个 List 类型的双向链表,然后通过它的一些方法往里面塞了一些元素。可以往里面塞的元素的方法有 PushFront、PushBack、InsertAfter、InsertBefore 等。

    但是你发现没有,这些方法接受的新元素的类型都是 interface{} 的。也就是说,这个 List 类型的链表只接受 interface{} 类型的新元素值。

    而当新元素值进入链表之后,链表会把它们再包装成 list.Element 类型的值。你看,那些往里塞元素值的方法返回的都是被包装后的 *list.Element 类型的元素值。

    当你像我这样浏览了 container/list.List 类型的相关 API 之后,就应该可以明白我问这个问题的背景了。

    这个 List 类型只会接受 interface{} 类型的新元素值,并且只会吐出 *list.Element 类型的已有元素值。显然,任何移动已有元素值或者删除已有元素值的方法,都只会接受该链表自己吐出来的“Element 值”。因此,对于我们自己生成的“Element 值”,这个链表的任何方法都是不会接受的。

    当然了,如果你之前完全没用过 List 类型,可能会觉得这个问题有些突兀。但是当你看完下面的详细解释之后,我相信你就会有所了解了。

    我们这个专栏的一个风格就是:“先抛出问题,然后再解释前因后果”。目的就是,逼迫大家在碰到问题之后自己先去了解背景并试着找找答案,然后再回来看我的答案。这样的话,你对这些知识点的记忆会更牢固,不容易忘。

    我非常希望这个专栏能成为大家的“枕边书”,而不是听听音频就放在一边的那种。所以才有了这样的结构设计。如果你们能在今后碰到问题时想起这个专栏,到这里翻一翻并能找到答案的线索,那我就太高兴了。

    2019-04-23
    16
  • Err
    我觉得写一个实际的例子能帮助更好理解
    2018-10-23
    14
  • 会网络的老鼠
    现在大家写golang程序,一般用什么IDE?
    2018-08-30
    2
    7
  • fliter
    为什么不把list像slice,map一样作为一种不需要import其他包就能使用的数据类型?是因为使用场景较后两者比较少吗

    作者回复: 这需要一个过程,之前list也不是标准库中的一员。况且也没必要把太多的东西多做到语言里,这样反倒不利于后面的扩展。

    2018-08-29
    7
  • 公众号「后端进阶」
    前面的网友,goland了解一下,超赞的ide
    2018-09-06
    5
  • 云学
    在内存上,和ring的区别是list多了一个特殊表头节点,充当哨兵
    2018-08-31
    5
  • Geek_a8be59
    您好 能否出一个list 链表生成的一个图解,现在我看源码用图去模拟生成 一直搞混掉,特别是在初始化的时候prev和next都指向自身的root 这个很迷糊
    比如:
    c.PushBack("123")
    c.PushFront("456")
    c.PushFront("789")
    根据个人图解应该是789-》456-》nil,为什么能遍历出来很不清楚。能否有一个从初始化到最后生成的样例看一下 万分感谢

    作者回复: 按照你这三行代码,应该是 789 -> 456 -> 123 啊,你那个“789-》456-》ni”是怎么出来的?

    这个链表里所谓的 root 就是用来表示链表两端的尽头的。所以,这个链表的末端实际上并不是 nil ,而是 root。只是在 Element 的 Next 方法中,如果发现它的 next 字段的值等于 root,就会返回 nil 而已。

    明白了吗?

    2019-06-26
    1
    2
  • 李斌
    用 vscode 就蛮好的,我之前是八年 vim 党,写 golang 时硬生生地被掰成 vscode

    作者回复: 嗯,也算是与时俱进吧,未来有脑机接口了,也就用不着这些了。

    2018-10-30
    1
    2
  • Zer0
    不能把自己生成的Element传给List主要是因为Element的list成员是不开放的,我们不能操作,而在List上操作Element的时候是会判断Element的list成员是否是自己。是这样吗?

    作者回复: ✅

    2019-08-06
    1
  • jackstraw
    我尝试打印了 “var l = list.New()” 与 “var l list.List”两种方式的l类型,发现是不一样的,但是下面的操作却都是可以的
    func main() {
        //l := list.New()
        var l list.List
        e4 := l.PushBack(4)
        e1 := l.PushFront(1)
        l.InsertBefore(3, e4)
        l.InsertAfter(2, e1)
        //travel(l)
        travel(&l)
    }

    作者回复: 当然不一样,list.New 返回的是指针值。另外你可以再看看讲结构体和方法那篇文章。

    2019-07-09
    1
  • Sky
    这一讲没有实例代码

    作者回复: 建议大家在阅读这一篇文章时对照着 container 包的文档看。对这几个类型的 API 有一定了解之后,使用就是水到渠成的事情了。

    自己先试一试,如果有具体的问题,可以来这里问。

    2019-06-12
    1
  • 缘木求鱼
    又比如,在用于删除元素、移动元素,以及一些用于插入元素的方法中,只要判断一下传入的元素中指向所属链表的指针,是否与当前链表的指针相等就可以了。 这里传入的元素的所属链表指针是如何赋值的
    2018-10-21
    1
  • 窝窝头
    1.list作为队列,先进后出,ring可以应用于循环选择场景,对一些长时间占用资源的程序或者请求等对象进行处理
    2.heap还可以做排序,或者字符编码之类的
    2019-05-05
  • 兔子高
    你好,有个问题想问一下,你在文中有说每次判断链表是否初始化很浪费性能,但是你后面又说每次判断链表的长度或者它是否为空,问题如下
    1.如何判断是否初始化
    2.判断初始化和判断为空的区别
    3.判断链表长度和是否为空比判断是否初始化更节约性能是吗?性能大概会节约多少倍呢?
    麻烦解答一下,谢谢

    作者回复: 我这些是对照list源码说的,你可以去看一看list的源码,这些问题就都迎刃而解了。

    2018-09-05
  • xlh
    Slice 缩容策略是什么
    2018-08-29
收起评论
18
返回
顶部