Lang Golang】 análisis de código fuente de contenedor / lista

Es muy refrescante ver golang después de ver cpp. .

Primero mire la estructura de datos, hay dos elementos y una lista:

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

type List struct {
	root Element
	len  int
}

Super aerodinámico.

Mire las dos funciones transversales de Element:

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
}

Bueno, puede haber dudas sobre lo que está relacionado con el nodo list.root al recorrer.

De acuerdo con la definición de Lista, la suposición puede ser que se guarde un nodo vacío fijo, o tal vez una lista circular.

Mira la inicialización de la lista:

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

Efectivamente Sigue leyendo .

Las siguientes dos funciones simples, sin duda:

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

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

Mirando dos funciones:

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 () devuelve el nodo principal y Back devuelve el nodo de cola.

Inicialización perezosa, sin duda:

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

Operación de inserción, 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)
}

Eliminar operación:

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 eliminar, parece que Golang no necesita eliminar.

Mueve e al final de 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
}

Elimine la función de la lista vinculada y devuelva el valor:

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

También hay algunas funciones de contenedor:

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

También hay una función de lista, que es relativamente 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)
	}
}

La lista es relativamente simple. .

El punto importante es que se implementa la lista circular, lo que lleva a la posterior inserción y eliminación es más conveniente.

lazyInit lazy loading se utiliza principalmente para la Lista creada externamente. Las llamadas nuevas en el paquete se inicializarán automáticamente.

424 artículos originales publicados · Me gustaron 14 · Visitas 100,000+

Supongo que te gusta

Origin blog.csdn.net/LU_ZHAO/article/details/105483792
Recomendado
Clasificación