Go 语言核心 36 讲
郝林
《Go 并发编程实战》作者,前轻松筹大数据负责人
78704 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 55 讲
Go 语言核心 36 讲
15
15
1.0x
00:00/00:00
登录|注册

08 | container包中的那些容器

我们在上次讨论了数组和切片,当我们提到数组的时候,往往会想起链表。那么 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/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Go 语言核心 36 讲》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(37)

  • 最新
  • 精选
  • 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 类型,可能会觉得这个问题有些突兀。但是当你看完下面的详细解释之后,我相信你就会有所了解了。 我们这个专栏的一个风格就是:“先抛出问题,然后再解释前因后果”。目的就是,逼迫大家在碰到问题之后自己先去了解背景并试着找找答案,然后再回来看我的答案。这样的话,你对这些知识点的记忆会更牢固,不容易忘。 我非常希望这个专栏能成为大家的“枕边书”,而不是听听音频就放在一边的那种。所以才有了这样的结构设计。如果你们能在今后碰到问题时想起这个专栏,到这里翻一翻并能找到答案的线索,那我就太高兴了。

    5
    40
  • fliter
    为什么不把list像slice,map一样作为一种不需要import其他包就能使用的数据类型?是因为使用场景较后两者比较少吗

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

    2
    10
  • 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 而已。 明白了吗?

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

    作者回复: ✅

    5
  • 李斌
    用 vscode 就蛮好的,我之前是八年 vim 党,写 golang 时硬生生地被掰成 vscode

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

    2
    5
  • 雷朝建
    老师, 我看了一下list.go的源码,发现一个疑问是:延迟初始化的含义就是调用lazyInit,它的一个判断条件是:l.root.next==nil; 但是我们在使用list时候,不是先调用New函数吗?那么不应该会出现l.root.next为nil的情况的。 什么时候回出现l.root.next==nil, 从而导致源码中每次的PushFront等操作调用lazyInit呢?

    作者回复: List 是一个 struct 啊,有了 lazyInit 你就可以直接 var myList list.List 了啊。这不是就做到开箱即用了吗?

    3
  • 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 返回的是指针值。另外你可以再看看讲结构体和方法那篇文章。

    3
  • 杨震
    以后再有课的话 希望老师多加点图 虽然费点事 但应该更多为学员着想吧。文字阐述一点也不直观。

    作者回复: OK。

    2
  • Sky
    这一讲没有实例代码

    作者回复: 建议大家在阅读这一篇文章时对照着 container 包的文档看。对这几个类型的 API 有一定了解之后,使用就是水到渠成的事情了。 自己先试一试,如果有具体的问题,可以来这里问。

    2
  • 白有才
    这课程就像小说里的武功秘籍, 看了你不一定会练, 所以练成绝世武功的人就少之又少

    作者回复: 人生的终极意义不就是练成绝世武功嘛!;-)

收起评论
显示
设置
留言
37
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部