github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/docs/003-container包源码分析.md (about) 1 2 ## Container 容器数据类型 3 4 Container 主要实现了三个数据结构: 堆, 链表, 环 5 6 [分析示例:coding/container](../coding/container) 7 8 [源码位置:/src/container](../go/src/container) 9 10 ### Heap 最小堆 11 12 Heap 包定义了堆的实现接口,提供了 堆接口Push, Pop 并继承sort.Interface 的 Len, Less, Swap接口 , 所以实现一个最小堆, 需要实现上面五个接口; 13 14 需要注意, Pop 时, heap 接口会先将堆顶堆尾数据交换,所以实现 Pop 接口时,读取堆顶元素,实际是读取数组的最后一个元素 15 16 Heap 主要提供了以下方法列表 17 18 h 是自定义数据结构的指针 19 20 - heap.Init(h) 初始化最小堆 21 22 - heap.Push(h) 向堆中添加数据 23 24 - heap.Pop(h) 删除并返回堆顶数据 25 26 - heap.Remove(h, i) 删除第i个数据元素 27 28 - heap.Fix(h, i) 在外部修改了i元素值后, Fix重新堆化, 调整成最小堆 29 30 ### 堆的使用场景 31 32 - 构建优先队列 33 34 - 支持堆排序 35 36 - 快速找出一个集合中的最小值(或者最大值) 37 38 39 ### List 双链表 40 41 List 提供了对链表的实现, list.go 中提供两个元素, List 和 Element , 其中 List 实现了一个双向链表, Element表示链表中元素的结构 42 43 > <font color='red'>注意: container/list默认不是线程安全的,要保证数据安全,那么可以使用Lock锁解决。</font> 44 45 ### 结构定义 46 47 ```go 48 type Element struct { 49 // Next and previous pointers in the doubly-linked list of elements. 50 // To simplify the implementation, internally a list l is implemented 51 // as a ring, such that &l.root is both the next element of the last 52 // list element (l.Back()) and the previous element of the first list 53 // element (l.Front()). 54 next, prev *Element // 上一个和下一个元素的指针 55 56 // The list to which this element belongs. 57 list *List // 元素所在的链表 58 59 // The value stored with this element. 60 Value interface{} // 元素值 61 } 62 63 type List struct { 64 // root 链表的根元素, 用来判断链表是否已经到了链尾了 65 root Element // sentinel list element, only &root, root.prev, and root.next are used 66 // 链表长度 67 len int // current list length excluding (this) sentinel element 68 } 69 ``` 70 71 ### 方法集 72 73 ```go 74 type Element 75 func (e *Element) Next() *Element // 返回该元素的下一个元素,如果没有下一个元素则返回 nil 76 func (e *Element) Prev() *Element // 返回该元素的前一个元素,如果没有前一个元素则返回nil 77 78 type List 79 // New, Init, 读取第一个, 最后一个元素, 获取链表长度, 删除一个元素 80 func New() *List // 返回一个初始化的list 81 func (l *List) Init() *List // list l 初始化或者清除 list l 82 func (l *List) Back() *Element // 获取list l的最后一个元素 83 func (l *List) Front() *Element // 获取list l的最后一个元素 84 func (l *List) Len() int // 获取 list l 的长度 85 func (l *List) Remove(e *Element) interface{} // 如果元素 e 属于list l,将其从 list 中删除,并返回元素 e 的值 86 87 // 插入一个元素, 插入到链表头部或者末尾 88 func (l *List) PushFront(v interface{}) *Element // 在 list l 的首部插入值为 v 的元素,并返回该元素 89 func (l *List) PushBack(v interface{}) *Element // 在 list l 的末尾插入值为 v 的元素,并返回该元素 90 91 // 在指定元素之前/之后插入一个新值v的元素 92 func (l *List) InsertAfter(v interface{}, mark *Element) *Element // 在 list l 中元素 mark 之后插入一个值为 v 的元素,并返回该元素,如果 mark 不是list中元素,则 list 不改变 93 func (l *List) InsertBefore(v interface{}, mark *Element) *Element // 在 list l 中元素 mark 之前插入一个值为 v 的元素,并返回该元素,如果 mark 不是list中元素,则 list 不改变 94 95 // 移动一个已经存在的元素到指定元素之前/之后 96 func (l *List) MoveAfter(e, mark *Element) // 将元素 e 移动到元素 mark 之后,如果元素e 或者 mark 不属于 list l,或者 e==mark,则 list l 不改变 97 func (l *List) MoveBefore(e, mark *Element) // 将元素 e 移动到元素 mark 之前,如果元素e 或者 mark 不属于 list l,或者 e==mark,则 list l 不改变 98 99 // 移动一个存在的元素到链表头部/末尾 100 func (l *List) MoveToBack(e *Element) // 将元素 e 移动到 list l 的末尾,如果 e 不属于list l,则list不改变 101 func (l *List) MoveToFront(e *Element) // 将元素 e 移动到 list l 的首部,如果 e 不属于list l,则list不改变 102 103 // 在链表头部/末尾 插入另外一个链表 104 func (l *List) PushBackList(other *List) // 在 list l 的尾部插入另外一个 list,其中l 和 other 可以相等 105 func (l *List) PushFrontList(other *List) // 在 list l 的首部插入另外一个 list,其中 l 和 other 可以相等 106 ``` 107 108 ### var l list.List 声明的变量 l 可以直接用吗? 值将会是什么呢? 109 110 - 可以的。这被称为 开箱即用。 这种通过语句 var l list.List 声明的链表 l 可以直接使用的原因就是在于它的 延迟初始化 机制。 111 112 - l将会是一个长度为 0 的链表,这个链表持有的根元素也将会是一个空壳,其中只会包含缺省的内容。 113 114 115 ### Ring 循环链表, 环 116 117 container/ring 包中的 Ring 类型实现的是一个循环链表,俗称的环。其实 List 在内部就是一个循环链表。它的根元素永远不会持有任何实际的元素值,而该元素的存在就是为了连接这个循环链表的首尾两端。 118 119 所以,也可以说:List 的零值是一个只包含了根元素,但不包含任何实际元素值的空链表。那么,既然 Ring 和 List 的本质上都是循环链表,它们到底有什么不同呢? 120 121 Ring 和 List 的不同有以下几种: 122 123 - Ring 类型的数据结构仅由它自身即可代表,而 List 类型则需要由它以及 Element 类型联合表示。这是表示方式上的不同,也是结构复杂度上的不同。 124 125 - 一个 Ring 类型的值严格来讲,只代表了其所属的循环链表中的一个元素,而一个 List 类型的值则代表了一个完整的链表。这是表示维度上的不同。 126 127 - 在创建并初始化一个 Ring 值得时候,我们可以指定它包含的元素数量,但是对于一个 List 值来说却不能这样做(也没必要这样做)。循环链表一旦被创建,其长度是不可变的。这是两个代码包中 New 函数在功能上的不同,也是两个类型在初始化值方面的第一个不同 128 129 - 仅通过 var r ring.Ring 语句声明的 r 将会是一个长度为 1 的循环链表,而 List 类型的零值则是一个长度为 0 的链表。别忘了,List 中的根元素不会持有实际元素的值,因此计算长度时不会包含它。这是两个类型在初始化值方面的第二个不同。 130 131 - Ring 值得 Len 方法的算法复杂度是 O(N) 的,而 List 值得 Len 方法的算法复杂度是 O(1)的。这是两者在性能方面最显而易见的差别。 132 133 ### 方法集 134 135 ```go 136 type Ring struct { 137 next, prev *Ring // 前后环指针 138 // 值,这个值在ring包中不会被处理 139 Value interface{} // for use by client; untouched by this library 140 } 141 142 143 type Ring 144 func New(n int) *Ring // 用于创建一个新的 Ring, 接收一个整形参数,用于初始化 Ring 的长度 145 func (r *Ring) Len() int // 环长度 146 147 func (r *Ring) Next() *Ring // 返回当前元素的下个元素 148 func (r *Ring) Prev() *Ring // 返回当前元素的上个元素 149 func (r *Ring) Move(n int) *Ring // 指针从当前元素开始向后移动或者向前(n 可以为负数) 150 151 // Link & Unlink 组合起来可以对多个链表进行管理 152 func (r *Ring) Link(s *Ring) *Ring // 将两个 ring 连接到一起 (r 不能为空) 153 func (r *Ring) Unlink(n int) *Ring // 从当前元素开始,删除 n 个元素 154 155 func (r *Ring) Do(f func(interface{})) // Do 会依次将每个节点的 Value 当作参数调用这个函数 f, 实际上这是策略方法的引用,通过传递不同的函数以在同一个 ring 上实现多种不同的操作。 156 ``` 157 158 ### 遍历ring 159 160 ```go 161 // 方式一 162 p := ring.Next() 163 // do something with the first element 164 165 for p != ring { 166 // do something with current element 167 p = p.Next() 168 } 169 170 // 方式二 171 ring.Do(func(i interface{}){ 172 // do something with current element 173 }) 174 175 // 方式三,四,五 ... 参考ring包提供的方式 176 ``` 177 178 可见通过ring.Do() 可以非常方便的将一组参数/规则 应用到函数中; 179 180 ring 包提供了很多使用示例可供参考 181