go Ring(环形链表)源码解读与应用

首先是环形链表的结构与初初始化

type Ring struct {
	next, prev *Ring
	Value      interface{} // for use by client; untouched by this library
}

func (r *Ring) init() *Ring {
	r.next = r
	r.prev = r
	return r
}

 环形链表是有n个带前后指针的节点形成的,他可以正序也可以倒序遍历。

他的初始化是指定一个节点,然后把next和prev都指向自己。

然后是新建一个双向环形链表,传入一个长度,返回链表指针。该链表里面的节点都是空的,可以存放任何东西(因为是空接口)

func New(n int) *Ring {
	if n <= 0 {
		return nil
	}
	r := new(Ring)
	p := r
	for i := 1; i < n; i++ {
		p.next = &Ring{prev: p}
		p = p.next
	}
	p.next = r
	r.prev = p
	return r
}

他的Next() 和 prev()方法也很简单,返回他后一个元素和前一个元素的指针。

// Next returns the next ring element. r must not be empty.
func (r *Ring) Next() *Ring {
	if r.next == nil {
		return r.init()
	}
	return r.next
}

// Prev returns the previous ring element. r must not be empty.
func (r *Ring) Prev() *Ring {
	if r.next == nil {
		return r.init()
	}
	return r.prev
}

 move方法。只是简单的指针移动

func (r *Ring) Move(n int) *Ring {
	if r.next == nil {
		return r.init()
	}
	switch {
	case n < 0:
		for ; n < 0; n++ {
			r = r.prev
		}
	case n > 0:
		for ; n > 0; n-- {
			r = r.next
		}
	}
	return r
}

假设当前指针指向1,顺时针方向为next方向,则move(1),就是把指针移到2位置,move(-1),就是把指针移到7位置

获取长度(注意for循环里第二个条件p!=r,判断是否是遍历的起始节点)

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方法是传入一个方法,然后让每个节点都去运行这个方法

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)
		}
	}
}

 link是吧两个不同的环形链表连接起来,如果r和s在同一个环形链表里,则会把r与s之间沿着next方向的元素移除。

unc (r *Ring) Link(s *Ring) *Ring {
	n := r.Next()
	if s != nil {
		p := s.Prev()
		// Note: Cannot use multiple assignment because
		// evaluation order of LHS is not specified.
		r.next = s
		s.prev = r
		n.prev = p
		p.next = n
	}
	return n
}

unlick是把当前节点之后的n个节点移除,调用的是link方法,原理可以参考上图同一个环形链表的link方法

func (r *Ring) Unlink(n int) *Ring {
	if n <= 0 {
		return nil
	}
	return r.Link(r.Move(n + 1))
}

应用场景:

交易所的历史成交交易。可以存在环形链表里,当有新的交易成交时,把指针像下移一个,然后把旧的成交订单替换掉。(这边只是在内存里存了一定数量的最新成交订单,所有会有覆盖的情况,当链表长度为50,此时有新的成交订单进来时,会把最旧的那个订单替换掉)


r := this.currentMtf.Next() 
r.Value = mtf

this.currentMtf = r

查询历史订单

//do方法链上每个节点都会执行。这边做的只是封装工作,和重点无关,同时,这边像next方向,
//把成交订单添加入items。但是当前指针的订单是最新的,currentMtf.Next()就是最旧的订单或者nil
//所有items是按旧到新排序
match.currentMtf.Next().Do(func(data interface{}) {
	if data != nil {
		mtf := data.(*msq.MtfData)
		item := TradeItem{mtf.MatchTime, mtf.Price, mtf.Amount, mtf.IsBuy}
		items = append(items, item)
	}
})

//因为要显示的订单成交情况是最新的订单再到旧的,所有再次把他数据倒过来
for i := len(items) - 1; i >= 0; i-- {
	depth--
	info.Trades = append(info.Trades, items[i])
	if depth == 0 {
		break
	}
}

猜你喜欢

转载自blog.csdn.net/qq_38020553/article/details/82685151