是双向链表。
实现上,有哨兵root
节点,即当链表为空时,还有一个哨兵元素。
实现多态的方式是Element
结构体内有一个interface{}
数据成员,可以用来存储任意类型的数据。其余链表和链表元素的代码都是系统库提供的。
container/list/example_test.go
演示了如何申请一个List,并对它进行一些插入操作后,如何遍历访问该List。
有一点非常值得思考,就是Go语言没有构造函数,从而导致的一些问题。
当一个结构体变量在使用前需要对它的数据成员做一些非zero out的初始化时(Go语言会对所有数据成员做zero out),
要么是提供一个New接口,接口返回一个结构体的指针类型,内部实现是实例化结构体变量后,然后做Init操作。但是从语意上来讲,这种变量是分配在堆上的。
要么是提供一个Init接口,坏处是要么用户在栈上声明了结构体变量时,还需要手动调用Init函数,增加了使用时成本。要么是在其他的方法内部做lazy init,这又增加了结构体的实现复杂度,以及小小的性能(毕竟lazy init每次都要判断是否已经init了)。
如何清空链表?
list并没有提供类似clear的方法,所以必须循环遍历删除。
这里有个坑,就是遍历时调用Remove删除自身Element时,自身Element的Next将被修改为nil,所以需要在调用Remove之前,把Next记录下来,才能继续访问下一个元素。
额外的开销
由于list是通过在应用层提供的item之外包装一层Element对象的方式实现的,所以即使你是同一个item不停的插入和删除list,依然会不停的new Element和删除Element,这点还是挺糟糕的。
List
提供的对外接口有:
常规操作:
1 | // 初始化List,这个接口并不是在使用List前非调用不可的,因为部分List的操作会在操作前判断List是否已经Init并做懒初始化 |
wrap类型的操作,对常规操作做的组合封装:
1 | // 将某个元素移动到头部 |
最后,还支持链表的拼接操作:
1 | // 将<other>链表插入<l>链表之后 |
Element提供的接口:
1 | // 可访问的数据成员,可强转成用户存入的类型 |
基于Go 1.11.4
本文完,作者yoko,尊重劳动人民成果,转载请注明原文出处: https://pengrl.com/p/51973/