【Golang】 container / list source code analysis

It's so refreshing to watch golang after watching cpp. .

First look at the data structure, there are two Element and List:

type Element struct {
	next, prev *Element
	list       *List
	Value      interface{}
}

type List struct {
	root Element
	len  int
}

Super streamlined.

Look at Element's two traversal functions:

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
}

Well, there may be doubts about what is related to the list.root node when traversing.

According to the definition of List, the guess may be that a fixed empty node is saved, or maybe a circular list.

Look at the initialization of the list:

func (l *List) Init() *List {
	l.root.next = &l.root
	l.root.prev = &l.root
	l.len = 0
	return l
}

func New() *List {
	return new(List).Init()
}

really. Keep reading. .

The next two simple functions, no doubt:

func New() *List {
	return new(List).Init()
}

func (l *List) Len() int {
	return l.len
}

Looking at two functions:

func (l *List) Front() *Element {
	if l.len == 0 {
		return nil
	}
	return l.root.next
}

func (l *List) Back() *Element {
	if l.len == 0 {
		return nil
	}
	return l.root.prev
}

Front () returns the head node, and Back returns the tail node.

Lazy initialization, no doubt:

func (l *List) lazyInit() {
	if l.root.next == nil {
		l.Init()
	}
}

Insert operation, simple:

func (l *List) insert(e, at *Element) *Element {
	e.prev = at
	e.next = at.next
	e.prev.next = e
	e.next.prev = e
	e.list = l
	l.len++
	return e
}

func (l *List) insertValue(v interface{}, at *Element) *Element {
	return l.insert(&Element{Value: v}, at)
}

Remove operation:

func (l *List) remove(e *Element) *Element {
	e.prev.next = e.next
	e.next.prev = e.prev
	e.next = nil
	e.prev = nil
	e.list = nil
	l.len--
	return e
}

No delete, it seems that golang does not need to delete.

Move e to the back of at:

func (l *List) move(e, at *Element) *Element {
	if e == at {
		return e
	}
	e.prev.next = e.next
	e.next.prev = e.prev

	e.prev = at
	e.next = at.next
	e.prev.next = e
	e.next.prev = e
	return e
}

Remove the function from the linked list and return the value:

func (l *List) Remove(e *Element) interface{} {
	if e.list == l {
		l.remove(e)
	}
	return e.Value
}

There are also some wrapper functions:

func (l *List) PushFront(v interface{}) *Element {
	l.lazyInit()
	return l.insertValue(v, &l.root)
}

func (l *List) PushBack(v interface{}) *Element {
	l.lazyInit()
	return l.insertValue(v, l.root.prev)
}

func (l *List) InsertBefore(v interface{}, mark *Element) *Element {
	if mark.list != l {
		return nil
	}
	return l.insertValue(v, mark.prev)
}

func (l *List) InsertAfter(v interface{}, mark *Element) *Element {
	if mark.list != l {
		return nil
	}
	return l.insertValue(v, mark)
}

func (l *List) MoveToFront(e *Element) {
	if e.list != l || l.root.next == e {
		return
	}
	l.move(e, &l.root)
}

func (l *List) MoveToBack(e *Element) {
	if e.list != l || l.root.prev == e {
		return
	}
	l.move(e, l.root.prev)
}

func (l *List) MoveBefore(e, mark *Element) {
	if e.list != l || e == mark || mark.list != l {
		return
	}
	l.move(e, mark.prev)
}

func (l *List) MoveAfter(e, mark *Element) {
	if e.list != l || e == mark || mark.list != l {
		return
	}
	l.move(e, mark)
}

There is also a list function, which is relatively simple:

func (l *List) PushBackList(other *List) {
	l.lazyInit()
	for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
		l.insertValue(e.Value, l.root.prev)
	}
}

func (l *List) PushFrontList(other *List) {
	l.lazyInit()
	for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
		l.insertValue(e.Value, &l.root)
	}
}

List is relatively simple. .

The important point is that the circular list is implemented, which leads to the later insertion and deletion is more convenient.

lazyInit lazy loading is mainly used for externally created List. Calling New in the package will be automatically initialized.

Published 424 original articles · Liked 14 · Visits 100,000+

Guess you like

Origin blog.csdn.net/LU_ZHAO/article/details/105483792