引言
在之前的文章Go中的雙向鏈表list的使用與從源碼分析:Go中的雙線鏈表list,我們一起看了Go中的容器list的使用與其源碼的實現原理,這一次,我們來看一下另一個容器,也就是標題中所述的ring
,Go中的環形鏈表,與container/list
一樣,ring
同樣位於包conatainer
中。
數據結構
還是老規矩,一開始先看一下ring中所使用的數據結構。其實ring中使用的結構比較少,只有一個叫做Ring
的結構體。
// 一個Ring是一個環形鏈表的元素,對於環而言,並不存在起始點或者終止點。
// 指向環中任一元素的指針都用作整個環的引用
// nil環指針指向空的環,對於環來說0值是一個只有一個nil元素的一個元素的環
type Ring struct {
// 分別指向下一個元素與上一個元素
next, prev *Ring
// 該元素內實際存儲的數據
Value interface{} // for use by client; untouched by this library
}
創建實例與初始化的方法
接下來是如何創建一個ring的實例與環的初始化方法
// 創建新的長度爲n的ring實例的方法
func New(n int) *Ring {
// 如果輸入的n爲小於零的負數,則直接返回nil
if n <= 0 {
return nil
}
// 首先創建一個Ring的實例r,並將指針p指向r
r := new(Ring)
p := r
// 從1到n - 1循環,創建n - 1個Ring的實例,並依次將其接起來
for i := 1; i < n; i++ {
// 這裏,創建了一個Ring的實例,創建時將新的實例的prev指向當前節點(等號後部分),並將當前節點p的next指針指向了新創建的節點(等號前的部分)
p.next = &Ring{prev: p}
// 將指針向後移
p = p.next
}
// 在創建完n個節點後,此時仍然是一個單向的鏈表,因此之後需要將該鏈表的首尾連接起來
p.next = r
r.prev = p
return r
}
// 環的初始化方法,將初始點的前一節點與後一節點的指針都指向初始節點,之後將初始節點的指針返回
func (r *Ring) init() *Ring {
r.next = r
r.prev = r
return r
}
環的遍歷方式
與list
中的每個元素一樣,ring
中的每個元素也是使用Next()
與Prev()
方法來進行向後與向前遍歷。
// Next()方法返回環中的下一個元素,注意:r不能爲空
func (r *Ring) Next() *Ring {
if r.next == nil {
return r.init()
}
return r.next
}
// Prev()方法返回環中的前一個元素,注意:r不能爲空
func (r *Ring) Prev() *Ring {
if r.next == nil {
return r.init()
}
return r.prev
}
移動元素指針
// 在環中移動n個元素,若n爲負數,則向後移動,若n大於等於0,則向前移動,若n比r的長度更大,則移動n % r.Len()個元素。
// 之後返回環元素,注意r不能爲空。
//
func (r *Ring) Move(n int) *Ring {
// 如果r的下一個節點爲nil的話,說明此時環的結構是已經被破壞的,因此對其進行初始化。
if r.next == nil {
return r.init()
}
// 判斷n的正負,來確定是向前移動還是向後移動。
switch {
case n < 0:
for ; n < 0; n++ {
r = r.prev
}
case n > 0:
for ; n > 0; n-- {
r = r.next
}
}
return r
}
連接
//將環r與環s連接起來,使得r.Next()成爲s並且返回r.Next()的初始值,r必須不能爲空。
// 日過r和s指向同一個環,則對其的連接會移除r和s之間的元素。被移除的元素會構成一個子環,並且結果是一個指向這個子環的引用(如果沒有元素被移除,則結果仍然是r.Next()的初始值,而不是nil)
// 如果r和s指向不同的環,將它們連接會創建一個單獨的環,環中s的元素會插入在r之後。結果會指向插入之後s的最後一個元素之後。
func (r *Ring) Link(s *Ring) *Ring {
// 將r的下一個元素取出
n := r.Next()
// 判斷s是否爲nil,如果是nil則直接返回n
if s != nil {
// 將s的前一元素取出s
p := s.Prev()
// 將r的後一節點置爲s,同時將r的原先的後一節點的前一節點置爲s原先的前一節點。
r.next = s
s.prev = r
n.prev = p
p.next = n
}
return n
}
// 將r之後的n個元素刪除,返回被刪除的子環,r不能爲空
func (r *Ring) Unlink(n int) *Ring {
if n <= 0 {
return nil
}
return r.Link(r.Move(n + 1))
}
其它
// 計算環的長度,它按與元素數量成比例的時間執行。
func (r *Ring) Len() int {
// 定義一個計數器
n := 0
if r != nil {
// 在第一個點對點置一
n = 1
// 通過循環,不斷向後判斷是否已經遍歷到環的起始點,是的化結束循環
for p := r.Next(); p != r; p = p.next {
n++
}
}
return n
}
// Do calls function f on each element of the ring, in forward order.
// The behavior of Do is undefined if f changes *r.
// 傳入一個函數,對環中的所有元素都執行一遍這個函數,執行的順序是正向的順序。
// 如果函數f改變* r,那麼Do的行爲是不確定的。
func (r *Ring) Do(f func(interface{})) {
if r != nil {
f(r.Value)
for p := r.Next(); p != r; p = p.next {
f(p.Value)
}
}
}