[Go]链表的相关知识
切片有着占用内存少喝创建便捷等特点,但它本质上还是数组。切片的一大好处是可以通过窗口快速地定位并获取或者修改底层数组中的元素。不过当删除切片中的元素的时候就没那么简单了。元素复制一般是免不了的,就算只删除一个元素有时也会造成大量元素的移动。另一方面在切片被频繁扩容的情况下,新的底层数组会不断产生,这时的内存分配的量以及元素复制的次数可能就很可观了。尤其是当没有一个合理、有效的缩容策略的时候,旧的底层数组无法被回收,新的底层数组中也会有大量无用的元素槽位。过度的内存浪费不但会降低程序的性能,还可能会使内存溢出,并导致程序本奔溃。
由此可见正确的使用切片很重要。那提到数组时,经常会想到链表。Go语言的链表实现在其标准库的container/list代码包中,这个包包含了两个公开的程序实体:List和Element。前者实现了一个双向链表,后者代表了链表中元素的结构。
List和Element都是结构体类型,结构体类型有一个特点,那就是它们的零值都会说拥有其特定结构,但没有任何定制化内容的值。这样值中能够的字段也都会被分别赋予各自类型的零值。广义来讲,所谓的零值就是指做了声明,但未做初始化的变量,被赋予缺省值。
例如 var a [2]int声明的是变量a的值将会是一个包含了两个0的整数数组。
那 var l list.list声明的变量l的值将会是什么呢?这个零值将会是一个长度为0的链表,这个链表持有的根元素也将会是一个空壳,其中只会包含缺省的内容。这个链表可以直接拿来用(开箱即用)
1、可以把自己生成的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值插入其中
在List包含的方法中,用于插入新元素的那些方法都只接受 interface{} 类型的值,这些方法在内部会使用Element值包装接受到的新元素。这样做正是为了避免直接使用我们自己生成的元素,主要原因是避免链表的内部关联遭到外界破坏。
func (l *List) Front() *Element //获取链表中最前端和最后端的元素
func (l *List) Front() *Element //获取链表中最前端和最后端的元素 func (l *List) InsertBefore (v interface{}, mark *Element) *Element //在指定的元素之前插入元素
func (l *List) InsertAfter (v interface{}, mark *Element) *Element //在指定的元素之后插入元素 func (l *List) PushFornt(v interface{}) *Element //在链表最前端插入元素
func (l *List) PushFornt(v interface{}) *Element //在链表最后端插入元素
· 上述的方法都会被一个Element值的指针作为结果返回,它们就是链表给的安全接口,拿到这些内部元素的指针,就可以调用前面提到的那些用于移动元素的方法了。
2、链表为什么可以做到开箱即用?
通过语句var l list.list声明的链表l可以直接使用么,这是怎么做到的
List这个结构体类型有两个字段,一个是Element类型的字段root,代表根元素,另一个是int类型的字段len,存储链表的长达,它们都是包级私有的,也就是使用者无法查看和修改它们。
像前面那样声明的l ,其字段root和len都会被赋予相应的零值。len的零值是0,正好可以证明该链表还未包含任何元素
Element类型包含了几个包级私有字段,分别用于存储前一个元素、后一个元素以及所属链表的指针值。另外还有一个叫Value的公开的字段,该字段的作用就是持有元素的实际值,它是interface{}类型。在Element类型的零值中,这些字段的值都会是nil。
其实单凭这样一个l是无法正常运作的,关键在于其“延迟初始化”机制,即把初始化操作延后,仅在实际需要时才进行
在链表实现中,一些方法是无需对是否初始化判断的,比如Front方法和Back方法,一旦发现链表长度为0就直接返回nil就好了。
原因在于链表的PushFront方法、PushBack方法、PushBackList方法以及PushFrontList方法总会先判断链表的状态,并在必要时进行初始。
当向一个空的链表中添加新元素时,肯定会调用这四个方法中的一个,这时新元素中指向所属链表的指针一定会被设定为当前链表的指针。所以指针相等是链表已经初始化的充分必要条件
3、Ring与List的区别在哪?
container/ring包中的Ring类型实现的是一个循环链表,即环,其实List在内部也是一个循环链表,它的根元素永远不会持有任何实际的元素值,而该元素的存在,就是为了连接这个循环链表的首尾两端。
其主要区别有以下几点:
1)Ring类型的数据结构仅由它自身即可代表,而List类型则需要由它以及Element类型联合表示,这是表示方式上的不同,也是结构复杂度上的不同
2)一个Ring类型严格来讲只代表了其所属的循环链表的一个元素,而一个List类型的值则代表了一个完整的链表,这是表示维度上的不同
3)在创建并初始化一个Ring值时,可指定它包含的元素数量,但List值却不能这么做
4)仅通过 var r ring.Ring语句声明的r将会是一个长度为1的循环链表,而List类型的零值则是一个长度为0的链表。
5)Ring值的Len方法的算法复杂度是O(N),List值的算法复杂度是O(1)
[Go]链表的相关知识的更多相关文章
- 【Python五篇慢慢弹(5)】类的继承案例解析,python相关知识延伸
类的继承案例解析,python相关知识延伸 作者:白宁超 2016年10月10日22:36:57 摘要:继<快速上手学python>一文之后,笔者又将python官方文档认真学习下.官方给 ...
- Java 容器相关知识全面总结
Java实用类库提供了一套相当完整的容器来帮助我们解决很多具体问题.因为我本身是一名Android开发者,包括我在内很多安卓开发,最拿手的就是ListView(RecycleView)+BaseAda ...
- 一张思维导图带你梳理HashMap相关知识
HashMap可以说是java中最常见也是最重要的key-value存储结构类,很多程序员可能经常用,但是不一定清楚这个类背后的数据结构和相关操作原理,为了复习HashMap相关的知识,今天花了一天的 ...
- 提问式复习:图文回顾 redo log 相关知识
原文链接:提问式复习:图文回顾 redo log 相关知识 1.如何提升 redo日志 的写性能? 为了保证 redo日志 不丢失,会在磁盘中开辟一块空间将日志保存起来.但是这样会有一个问题,磁盘的读 ...
- MySQL学习总结:提问式回顾 undo log 相关知识
原文链接:MySQL学习总结:提问式回顾 undo log 相关知识 1.redo 日志支持恢复重做,那么如果是回滚事务中的操作呢,也会有什么日志支持么? 也回滚已有操作,那么就是想撤销,对应的有撤销 ...
- 移动WEB像素相关知识
了解移动web像素的知识,主要是为了切图时心中有数.本文主要围绕一个问题:怎样根据设备厂商提供的屏幕尺寸和物理像素得到我们切图需要的逻辑像素?围绕这个问题以iphone5为例讲解涉及到的web像素相关 ...
- listener监听器的相关知识
从别人的博客上我学习了listener的相关知识现在分享给大家 1.概念: 监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上 ...
- UIViewController相关知识
title: UIViewController 相关知识date: 2015-12-13 11:50categories: IOS tags: UIViewController 小小程序猿我的博客:h ...
- 【转】java NIO 相关知识
原文地址:http://www.iteye.com/magazines/132-Java-NIO Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的 ...
随机推荐
- 145 Binary Tree Postorder Traversal 二叉树的后序遍历
给定一棵二叉树,返回其节点值的后序遍历.例如:给定二叉树 [1,null,2,3], 1 \ 2 / 3返回 [3,2,1].注意: 递归方法很简单,你可以使用迭代方法来解 ...
- [已读]编写高质量代码 改善JavaScript程序的188个建议
吐槽一万遍,买的最后悔的一本,没有之一,大量篇幅抄袭<高性能javascript>,我记得还有部分抄袭<javascript精粹>,<javascript模式>有没 ...
- Backbone学习记录(6)
路由 backbone将路由规则和一个方法名绑定到一起,来控制单页的hash,以及单页的前进后退. var UserRouter = Backbone.Router.extend({ routes: ...
- 1049 - One Way Roads 观察 dfs
http://lightoj.com/volume_showproblem.php?problem=1049 题意是,在一副有向图中,要使得它变成一个首尾相连的图,需要的最小代价. 就是本来是1--& ...
- Apache Kylin的框架介绍
不多说,直接上干货! Apache kylin 能提供低延迟(sub-second latency)的秘诀就是预计算,即针对一个星型拓扑结构的数据立方体,预计算多个维度组合的度量,然后将结果保存在hb ...
- Kotlin学习的一些笔记
Introduction 写在前面 关于本书 这本书适合你吗? 关于作者 介绍 什么是Kotlin? 我们通过Kotlin得到什么 准备工作 Android Studio 安装Kotlin插件 创建一 ...
- Java 利用FTP上传,下载文件,遍历文件目录
Java实现FTP上传下载文件的工具包有很多,这里我采用Java自带的API,实现FTP上传下载文件.另外JDK1.7以前的版本与其之后版本的API有了较大的改变了. 例如: JDK1.7之前 JDK ...
- 如何在Ubuntu里安装Helm
Helm是什么?在战网上玩过暗黑破坏神2代的程序员们应该还记得,Helm是国度的意思. 而在计算机领域,Helm是什么? Helm是Kubernetes的一个包管理工具,有点像nodejs的npm,U ...
- 机器学习在SAP Cloud for Customer中的应用
关于机器学习这个话题,我相信我这个公众号1500多位关注者里,一定有很多朋友的水平比Jerry高得多.如果您看过我以前两篇文章,您就会发现,我对机器学习仅仅停留在会使用API的层面上. 使用Java程 ...
- saltstack 源码安装
面向对象编程(oop) 面向对象: 面向对象三大特性: 封装 继承 多肽封装: 封装就是将具体的客观事物封装成抽象的类.并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可行的进行信息隐藏继承 ...