而且很多地方做了 lazyInit(很多地方也叫:延迟初始化),即在必要时才初始化链表(申请内存资源),因为 Go 本身也不知道你存的数据到底是个什么东西,如果存放的值很大,那么一锅脑初始化可能会花费很多时间,而且为了避免声明出来没人使用的尴尬局面,所以通过 lazyInit去分散初始化的操作带来的计算量和存储空间消耗。
比如集中声明非常多的大容量切片, CPU 和内存空间的使用量肯定都会一个激增,并且只有设法让其中的切片及其底层数组被回收,内存使用量才会有所降低。所以把初始化分散再配合 GC 可以更好的利用内存资源同时减少CPU压力。
其实,Go SDK 实现的这个链表不仅仅是双向链表,也可以看成是循环链表 List 的 root 元素恰好把链表的对头和队尾元素连起来,但是 root 本身并不存储元素,它仅仅是个“前哨”。
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
}
// insert 代表把 e 插到 at 的后面
func (l *List) insert(e, at *Element) *Element {
n := at.next // 保存at的后继
at.next = e // 将 at 后继指向 e
e.prev = at // 将 e 前驱指向 at
e.next = n // 将 e 后继指向原理at的后继
n.prev = e // 将 原来at的后继指向 e
e.list = l // 将 e 的 list 指针指向 l
l.len++ // 链表数量增加
return e
}
// insertValue 其实是对 insert 方法的封装
func (l *List) insertValue(v interface{}, at *Element) *Element {
return l.insert(&Element{Value: v}, at)
}
func (l *List) MoveAfter(e, mark *Element) {
if e.list != l || e == mark || mark.list != l {
return
}
l.move(e, mark)
}
func (l *List) move(e, at *Element) *Element {
if e == at {
return e
}
e.prev.next = e.next
e.next.prev = e.prev
n := at.next
at.next = e
e.prev = at
e.next = n
n.prev = e
return e
}