mirror of
https://gitee.com/johng/gf
synced 2026-06-06 02:25:47 +08:00
feat(container/glist): add generic list feature (#4483)
It is wrote with glist.List's and list.List's source codes and improve to support T type. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: hailaz <739476267@qq.com>
This commit is contained in:
721
container/glist/glist_t.go
Normal file
721
container/glist/glist_t.go
Normal file
@ -0,0 +1,721 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package glist
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/deepcopy"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// TElement is an element of a linked list.
|
||||
type TElement[T any] struct {
|
||||
// Next and previous pointers in the doubly-linked list of elements.
|
||||
// To simplify the implementation, internally a list l is implemented
|
||||
// as a ring, such that &l.root is both the next element of the last
|
||||
// list element (l.Back()) and the previous element of the first list
|
||||
// element (l.Front()).
|
||||
next, prev *TElement[T]
|
||||
|
||||
// The list to which this element belongs.
|
||||
list *TList[T]
|
||||
|
||||
// The value stored with this element.
|
||||
Value T
|
||||
}
|
||||
|
||||
// Next returns the next list element or nil.
|
||||
func (e *TElement[T]) Next() *TElement[T] {
|
||||
if p := e.next; e.list != nil && p != &e.list.root {
|
||||
return p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Prev returns the previous list element or nil.
|
||||
func (e *TElement[T]) Prev() *TElement[T] {
|
||||
if p := e.prev; e.list != nil && p != &e.list.root {
|
||||
return p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TList is a doubly linked list containing a concurrent-safe/unsafe switch.
|
||||
// The switch should be set when its initialization and cannot be changed then.
|
||||
|
||||
type TList[T any] struct {
|
||||
mu rwmutex.RWMutex
|
||||
root TElement[T] // sentinel list element, only &root, root.prev, and root.next are used
|
||||
len int // current list length excluding (this) sentinel element
|
||||
}
|
||||
|
||||
// NewT creates and returns a new empty doubly linked list.
|
||||
func NewT[T any](safe ...bool) *TList[T] {
|
||||
l := &TList[T]{
|
||||
mu: rwmutex.Create(safe...),
|
||||
}
|
||||
return l.init()
|
||||
}
|
||||
|
||||
// NewTFrom creates and returns a list from a copy of given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using list in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewTFrom[T any](array []T, safe ...bool) *TList[T] {
|
||||
l := NewT[T](safe...)
|
||||
for _, v := range array {
|
||||
l.insertValue(v, l.root.prev)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// PushFront inserts a new element `e` with value `v` at the front of list `l` and returns `e`.
|
||||
func (l *TList[T]) PushFront(v T) (e *TElement[T]) {
|
||||
l.mu.Lock()
|
||||
l.lazyInit()
|
||||
e = l.insertValue(v, &l.root)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// PushBack inserts a new element `e` with value `v` at the back of list `l` and returns `e`.
|
||||
func (l *TList[T]) PushBack(v T) (e *TElement[T]) {
|
||||
l.mu.Lock()
|
||||
l.lazyInit()
|
||||
e = l.insertValue(v, l.root.prev)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// PushFronts inserts multiple new elements with values `values` at the front of list `l`.
|
||||
func (l *TList[T]) PushFronts(values []T) {
|
||||
l.mu.Lock()
|
||||
l.lazyInit()
|
||||
for _, v := range values {
|
||||
l.insertValue(v, &l.root)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// PushBacks inserts multiple new elements with values `values` at the back of list `l`.
|
||||
func (l *TList[T]) PushBacks(values []T) {
|
||||
l.mu.Lock()
|
||||
l.lazyInit()
|
||||
for _, v := range values {
|
||||
l.insertValue(v, l.root.prev)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// PopBack removes the element from back of `l` and returns the value of the element.
|
||||
func (l *TList[T]) PopBack() (value T) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.lazyInit()
|
||||
if l.len == 0 {
|
||||
return
|
||||
}
|
||||
return l.remove(l.root.prev)
|
||||
}
|
||||
|
||||
// PopFront removes the element from front of `l` and returns the value of the element.
|
||||
func (l *TList[T]) PopFront() (value T) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.lazyInit()
|
||||
if l.len == 0 {
|
||||
return
|
||||
}
|
||||
return l.remove(l.root.next)
|
||||
}
|
||||
|
||||
// PopBacks removes `max` elements from back of `l`
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *TList[T]) PopBacks(max int) (values []T) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.lazyInit()
|
||||
|
||||
length := l.len
|
||||
if length > 0 {
|
||||
if max > 0 && max < length {
|
||||
length = max
|
||||
}
|
||||
values = make([]T, length)
|
||||
for i := 0; i < length; i++ {
|
||||
values[i] = l.remove(l.root.prev)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PopFronts removes `max` elements from front of `l`
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *TList[T]) PopFronts(max int) (values []T) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.lazyInit()
|
||||
|
||||
length := l.len
|
||||
if length > 0 {
|
||||
if max > 0 && max < length {
|
||||
length = max
|
||||
}
|
||||
values = make([]T, length)
|
||||
for i := 0; i < length; i++ {
|
||||
values[i] = l.remove(l.root.next)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PopBackAll removes all elements from back of `l`
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *TList[T]) PopBackAll() []T {
|
||||
return l.PopBacks(-1)
|
||||
}
|
||||
|
||||
// PopFrontAll removes all elements from front of `l`
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *TList[T]) PopFrontAll() []T {
|
||||
return l.PopFronts(-1)
|
||||
}
|
||||
|
||||
// FrontAll copies and returns values of all elements from front of `l` as slice.
|
||||
func (l *TList[T]) FrontAll() (values []T) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
l.lazyInit()
|
||||
length := l.len
|
||||
if length > 0 {
|
||||
values = make([]T, length)
|
||||
for i, e := 0, l.front(); i < length; i, e = i+1, e.Next() {
|
||||
values[i] = e.Value
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BackAll copies and returns values of all elements from back of `l` as slice.
|
||||
func (l *TList[T]) BackAll() (values []T) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
l.lazyInit()
|
||||
length := l.len
|
||||
if length > 0 {
|
||||
values = make([]T, length)
|
||||
for i, e := 0, l.back(); i < length; i, e = i+1, e.Prev() {
|
||||
values[i] = e.Value
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FrontValue returns value of the first element of `l` or zero value of T if the list is empty.
|
||||
func (l *TList[T]) FrontValue() (value T) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
l.lazyInit()
|
||||
if e := l.front(); e != nil {
|
||||
value = e.Value
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BackValue returns value of the last element of `l` or zero value of T if the list is empty.
|
||||
func (l *TList[T]) BackValue() (value T) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
l.lazyInit()
|
||||
if e := l.back(); e != nil {
|
||||
value = e.Value
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Front returns the first element of list `l` or nil if the list is empty.
|
||||
func (l *TList[T]) Front() (e *TElement[T]) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
|
||||
l.lazyInit()
|
||||
|
||||
e = l.front()
|
||||
return
|
||||
}
|
||||
|
||||
// Back returns the last element of list `l` or nil if the list is empty.
|
||||
func (l *TList[T]) Back() (e *TElement[T]) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
l.lazyInit()
|
||||
|
||||
e = l.back()
|
||||
return
|
||||
}
|
||||
|
||||
// Len returns the number of elements of list `l`.
|
||||
// The complexity is O(1).
|
||||
func (l *TList[T]) Len() (length int) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
|
||||
l.lazyInit()
|
||||
|
||||
length = l.len
|
||||
return
|
||||
}
|
||||
|
||||
// Size is alias of Len.
|
||||
func (l *TList[T]) Size() int {
|
||||
return l.Len()
|
||||
}
|
||||
|
||||
// MoveBefore moves element `e` to its new position before `p`.
|
||||
// If `e` or `p` is not an element of `l`, or `e` == `p`, the list is not modified.
|
||||
// The element and `p` must not be nil.
|
||||
func (l *TList[T]) MoveBefore(e, p *TElement[T]) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
l.lazyInit()
|
||||
|
||||
if e.list != l || e == p || p.list != l {
|
||||
return
|
||||
}
|
||||
l.move(e, p.prev)
|
||||
}
|
||||
|
||||
// MoveAfter moves element `e` to its new position after `p`.
|
||||
// If `e` or `p` is not an element of `l`, or `e` == `p`, the list is not modified.
|
||||
// The element and `p` must not be nil.
|
||||
func (l *TList[T]) MoveAfter(e, p *TElement[T]) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.lazyInit()
|
||||
if e.list != l || e == p || p.list != l {
|
||||
return
|
||||
}
|
||||
l.move(e, p)
|
||||
}
|
||||
|
||||
// MoveToFront moves element `e` to the front of list `l`.
|
||||
// If `e` is not an element of `l`, the list is not modified.
|
||||
// The element must not be nil.
|
||||
func (l *TList[T]) MoveToFront(e *TElement[T]) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.lazyInit()
|
||||
|
||||
if e.list != l || l.root.next == e {
|
||||
return
|
||||
}
|
||||
// see comment in List.Remove about initialization of l
|
||||
l.move(e, &l.root)
|
||||
}
|
||||
|
||||
// MoveToBack moves element `e` to the back of list `l`.
|
||||
// If `e` is not an element of `l`, the list is not modified.
|
||||
// The element must not be nil.
|
||||
func (l *TList[T]) MoveToBack(e *TElement[T]) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.lazyInit()
|
||||
|
||||
if e.list != l || l.root.prev == e {
|
||||
return
|
||||
}
|
||||
// see comment in List.Remove about initialization of l
|
||||
l.move(e, l.root.prev)
|
||||
}
|
||||
|
||||
// PushBackList inserts a copy of an other list at the back of list `l`.
|
||||
// The lists `l` and `other` may be the same, but they must not be nil.
|
||||
func (l *TList[T]) PushBackList(other *TList[T]) {
|
||||
if l != other {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
l.lazyInit()
|
||||
|
||||
for i, e := other.len, other.front(); i > 0; i, e = i-1, e.Next() {
|
||||
l.insertValue(e.Value, l.root.prev)
|
||||
}
|
||||
}
|
||||
|
||||
// PushFrontList inserts a copy of an other list at the front of list `l`.
|
||||
// The lists `l` and `other` may be the same, but they must not be nil.
|
||||
func (l *TList[T]) PushFrontList(other *TList[T]) {
|
||||
if l != other {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
l.lazyInit()
|
||||
|
||||
for i, e := other.len, other.back(); i > 0; i, e = i-1, e.Prev() {
|
||||
l.insertValue(e.Value, &l.root)
|
||||
}
|
||||
}
|
||||
|
||||
// InsertAfter inserts a new element `e` with value `v` immediately after `p` and returns `e`.
|
||||
// If `p` is not an element of `l`, the list is not modified.
|
||||
// The `p` must not be nil.
|
||||
func (l *TList[T]) InsertAfter(p *TElement[T], v T) (e *TElement[T]) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
l.lazyInit()
|
||||
if p.list != l {
|
||||
return nil
|
||||
}
|
||||
e = l.insertValue(v, p)
|
||||
return
|
||||
}
|
||||
|
||||
// InsertBefore inserts a new element `e` with value `v` immediately before `p` and returns `e`.
|
||||
// If `p` is not an element of `l`, the list is not modified.
|
||||
// The `p` must not be nil.
|
||||
func (l *TList[T]) InsertBefore(p *TElement[T], v T) (e *TElement[T]) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
l.lazyInit()
|
||||
if p.list != l {
|
||||
return nil
|
||||
}
|
||||
e = l.insertValue(v, p.prev)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove removes `e` from `l` if `e` is an element of list `l`.
|
||||
// It returns the element value e.Value.
|
||||
// The element must not be nil.
|
||||
func (l *TList[T]) Remove(e *TElement[T]) (value T) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.lazyInit()
|
||||
return l.remove(e)
|
||||
}
|
||||
|
||||
// Removes removes multiple elements `es` from `l` if `es` are elements of list `l`.
|
||||
func (l *TList[T]) Removes(es []*TElement[T]) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.lazyInit()
|
||||
for _, e := range es {
|
||||
l.remove(e)
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveAll removes all elements from list `l`.
|
||||
func (l *TList[T]) RemoveAll() {
|
||||
l.mu.Lock()
|
||||
l.init()
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// Clear is alias of RemoveAll.
|
||||
func (l *TList[T]) Clear() {
|
||||
l.RemoveAll()
|
||||
}
|
||||
|
||||
// ToList converts TList[T] to list.List
|
||||
func (l *TList[T]) ToList() *list.List {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
|
||||
return l.toList()
|
||||
}
|
||||
|
||||
// toList converts TList[T] to list.List
|
||||
func (l *TList[T]) toList() *list.List {
|
||||
l.lazyInit()
|
||||
|
||||
nl := list.New()
|
||||
|
||||
for e := l.front(); e != nil; e = e.Next() {
|
||||
nl.PushBack(e.Value)
|
||||
}
|
||||
return nl
|
||||
}
|
||||
|
||||
// AppendList append list.List to the end
|
||||
func (l *TList[T]) AppendList(nl *list.List) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
l.appendList(nl)
|
||||
}
|
||||
|
||||
// appendList append list.List to the end
|
||||
func (l *TList[T]) appendList(nl *list.List) {
|
||||
if nl.Len() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
l.lazyInit()
|
||||
|
||||
for e := nl.Front(); e != nil; e = e.Next() {
|
||||
if v, ok := e.Value.(T); ok {
|
||||
l.insertValue(v, l.root.prev)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AssignList assigns list.List to now TList[T].
|
||||
// It will clear TList[T] first, and append the list.List.
|
||||
// Note: Elements in nl that are not assignable to T are silently skipped.
|
||||
// Returns the number of skipped (incompatible) elements.
|
||||
func (l *TList[T]) AssignList(nl *list.List) int {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
return l.assignList(nl)
|
||||
}
|
||||
|
||||
// assignList assigns list.List to now TList[T].
|
||||
// It will clear TList[T] first, and append the list.List.
|
||||
// Returns the number of skipped (incompatible) elements.
|
||||
func (l *TList[T]) assignList(nl *list.List) int {
|
||||
l.init()
|
||||
if nl.Len() == 0 {
|
||||
return 0
|
||||
}
|
||||
skipped := 0
|
||||
for e := nl.Front(); e != nil; e = e.Next() {
|
||||
if v, ok := e.Value.(T); ok {
|
||||
l.insertValue(v, l.root.prev)
|
||||
} else {
|
||||
skipped++
|
||||
}
|
||||
}
|
||||
return skipped
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function `f` within RWMutex.RLock.
|
||||
func (l *TList[T]) RLockFunc(f func(list *list.List)) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
|
||||
f(l.toList())
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function `f` within RWMutex.Lock.
|
||||
func (l *TList[T]) LockFunc(f func(list *list.List)) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
nl := l.toList()
|
||||
f(nl)
|
||||
l.assignList(nl)
|
||||
}
|
||||
|
||||
// Iterator is alias of IteratorAsc.
|
||||
func (l *TList[T]) Iterator(f func(e *TElement[T]) bool) {
|
||||
l.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the list readonly in ascending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (l *TList[T]) IteratorAsc(f func(e *TElement[T]) bool) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
l.lazyInit()
|
||||
length := l.len
|
||||
if length > 0 {
|
||||
for i, e := 0, l.front(); i < length; i, e = i+1, e.Next() {
|
||||
if !f(e) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the list readonly in descending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (l *TList[T]) IteratorDesc(f func(e *TElement[T]) bool) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
l.lazyInit()
|
||||
length := l.len
|
||||
if length > 0 {
|
||||
for i, e := 0, l.back(); i < length; i, e = i+1, e.Prev() {
|
||||
if !f(e) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Join joins list elements with a string `glue`.
|
||||
func (l *TList[T]) Join(glue string) string {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
l.lazyInit()
|
||||
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
length := l.len
|
||||
if length > 0 {
|
||||
for i, e := 0, l.front(); i < length; i, e = i+1, e.Next() {
|
||||
buffer.WriteString(gconv.String(e.Value))
|
||||
if i != length-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// String returns current list as a string.
|
||||
func (l *TList[T]) String() string {
|
||||
if l == nil {
|
||||
return ""
|
||||
}
|
||||
return "[" + l.Join(",") + "]"
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (l TList[T]) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(l.FrontAll())
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (l *TList[T]) UnmarshalJSON(b []byte) error {
|
||||
var array []T
|
||||
if err := json.UnmarshalUseNumber(b, &array); err != nil {
|
||||
return err
|
||||
}
|
||||
l.init()
|
||||
l.PushBacks(array)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for list.
|
||||
func (l *TList[T]) UnmarshalValue(value any) (err error) {
|
||||
var array []T
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.UnmarshalUseNumber(gconv.Bytes(value), &array)
|
||||
default:
|
||||
anyArray := gconv.SliceAny(value)
|
||||
if err = gconv.Scan(anyArray, &array); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
l.init()
|
||||
l.PushBacks(array)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (l *TList[T]) DeepCopy() any {
|
||||
if l == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
|
||||
l.lazyInit()
|
||||
|
||||
var (
|
||||
length = l.len
|
||||
valuesT = make([]T, length)
|
||||
)
|
||||
if length > 0 {
|
||||
for i, e := 0, l.front(); i < length; i, e = i+1, e.Next() {
|
||||
valuesT[i] = deepcopy.Copy(e.Value).(T)
|
||||
}
|
||||
}
|
||||
return NewTFrom(valuesT, l.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Init initializes or clears list l.
|
||||
func (l *TList[T]) init() *TList[T] {
|
||||
l.root.next = &l.root
|
||||
l.root.prev = &l.root
|
||||
l.len = 0
|
||||
return l
|
||||
}
|
||||
|
||||
// lazyInit lazily initializes a zero List value.
|
||||
func (l *TList[T]) lazyInit() {
|
||||
if l.root.next == nil {
|
||||
l.init()
|
||||
}
|
||||
}
|
||||
|
||||
// insert inserts e after at, increments l.len, and returns e.
|
||||
func (l *TList[T]) insert(e, at *TElement[T]) *TElement[T] {
|
||||
e.prev = at
|
||||
e.next = at.next
|
||||
e.prev.next = e
|
||||
e.next.prev = e
|
||||
e.list = l
|
||||
l.len++
|
||||
return e
|
||||
}
|
||||
|
||||
// insertValue is a convenience wrapper for insert(&Element{Value: v}, at).
|
||||
func (l *TList[T]) insertValue(v T, at *TElement[T]) *TElement[T] {
|
||||
return l.insert(&TElement[T]{Value: v}, at)
|
||||
}
|
||||
|
||||
// remove removes e from its list, decrements l.len
|
||||
func (l *TList[T]) remove(e *TElement[T]) (val T) {
|
||||
if e.list != l {
|
||||
return
|
||||
}
|
||||
e.prev.next = e.next
|
||||
e.next.prev = e.prev
|
||||
e.next = nil // avoid memory leaks
|
||||
e.prev = nil // avoid memory leaks
|
||||
e.list = nil
|
||||
l.len--
|
||||
|
||||
return e.Value
|
||||
}
|
||||
|
||||
// move moves e to next to at.
|
||||
func (l *TList[T]) move(e, at *TElement[T]) {
|
||||
if e == at {
|
||||
return
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// front returns the first element of list l or nil if the list is empty.
|
||||
func (l *TList[T]) front() *TElement[T] {
|
||||
if l.len == 0 {
|
||||
return nil
|
||||
}
|
||||
return l.root.next
|
||||
}
|
||||
|
||||
// back returns the last element of list l or nil if the list is empty.
|
||||
func (l *TList[T]) back() *TElement[T] {
|
||||
if l.len == 0 {
|
||||
return nil
|
||||
}
|
||||
return l.root.prev
|
||||
}
|
||||
61
container/glist/glist_z_bench_t_test.go
Normal file
61
container/glist/glist_z_bench_t_test.go
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
package glist
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
lt = NewT[any](true)
|
||||
)
|
||||
|
||||
func Benchmark_T_PushBack(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
lt.PushBack(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_T_PushFront(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
lt.PushFront(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_T_Len(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
lt.Len()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_T_PopFront(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
lt.PopFront()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_T_PopBack(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
lt.PopBack()
|
||||
}
|
||||
})
|
||||
}
|
||||
689
container/glist/glist_z_example_t_test.go
Normal file
689
container/glist/glist_z_example_t_test.go
Normal file
@ -0,0 +1,689 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package glist_test
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/glist"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
func ExampleNewT() {
|
||||
n := 10
|
||||
l := glist.NewT[any]()
|
||||
for i := 0; i < n; i++ {
|
||||
l.PushBack(i)
|
||||
}
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.FrontAll())
|
||||
fmt.Println(l.BackAll())
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
fmt.Print(l.PopFront())
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println(l.Len())
|
||||
|
||||
// Output:
|
||||
// 10
|
||||
// [0,1,2,3,4,5,6,7,8,9]
|
||||
// [0 1 2 3 4 5 6 7 8 9]
|
||||
// [9 8 7 6 5 4 3 2 1 0]
|
||||
// 0123456789
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleNewTFrom() {
|
||||
n := 10
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 10, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.FrontAll())
|
||||
fmt.Println(l.BackAll())
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
fmt.Print(l.PopFront())
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println(l.Len())
|
||||
|
||||
// Output:
|
||||
// 10
|
||||
// [1,2,3,4,5,6,7,8,9,10]
|
||||
// [1 2 3 4 5 6 7 8 9 10]
|
||||
// [10 9 8 7 6 5 4 3 2 1]
|
||||
// 12345678910
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleTList_PushFront() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.PushFront(0)
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 6
|
||||
// [0,1,2,3,4,5]
|
||||
}
|
||||
|
||||
func ExampleTList_PushBack() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.PushBack(6)
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 6
|
||||
// [1,2,3,4,5,6]
|
||||
}
|
||||
|
||||
func ExampleTList_PushFronts() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.PushFronts(g.Slice{0, -1, -2, -3, -4})
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 10
|
||||
// [-4,-3,-2,-1,0,1,2,3,4,5]
|
||||
}
|
||||
|
||||
func ExampleTList_PushBacks() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.PushBacks(g.Slice{6, 7, 8, 9, 10})
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 10
|
||||
// [1,2,3,4,5,6,7,8,9,10]
|
||||
}
|
||||
|
||||
func ExampleTList_PopBack() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.PopBack())
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 5
|
||||
// 4
|
||||
// [1,2,3,4]
|
||||
}
|
||||
|
||||
func ExampleTList_PopFront() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.PopFront())
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 1
|
||||
// 4
|
||||
// [2,3,4,5]
|
||||
}
|
||||
|
||||
func ExampleTList_PopBacks() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.PopBacks(2))
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// [5 4]
|
||||
// 3
|
||||
// [1,2,3]
|
||||
}
|
||||
|
||||
func ExampleTList_PopFronts() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.PopFronts(2))
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// [1 2]
|
||||
// 3
|
||||
// [3,4,5]
|
||||
}
|
||||
|
||||
func ExampleTList_PopBackAll() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.PopBackAll())
|
||||
fmt.Println(l.Len())
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// [5 4 3 2 1]
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleTList_PopFrontAll() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.PopFrontAll())
|
||||
fmt.Println(l.Len())
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// [1 2 3 4 5]
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleTList_FrontAll() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.FrontAll())
|
||||
|
||||
// Output:
|
||||
// [1,2,3,4,5]
|
||||
// [1 2 3 4 5]
|
||||
}
|
||||
|
||||
func ExampleTList_BackAll() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.BackAll())
|
||||
|
||||
// Output:
|
||||
// [1,2,3,4,5]
|
||||
// [5 4 3 2 1]
|
||||
}
|
||||
|
||||
func ExampleTList_FrontValue() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.FrontValue())
|
||||
|
||||
// Output:
|
||||
// [1,2,3,4,5]
|
||||
// 1
|
||||
}
|
||||
|
||||
func ExampleTList_BackValue() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l)
|
||||
fmt.Println(l.BackValue())
|
||||
|
||||
// Output:
|
||||
// [1,2,3,4,5]
|
||||
// 5
|
||||
}
|
||||
|
||||
func ExampleTList_Front() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Front().Value)
|
||||
fmt.Println(l)
|
||||
|
||||
e := l.Front()
|
||||
l.InsertBefore(e, 0)
|
||||
l.InsertAfter(e, "a")
|
||||
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// [1,2,3,4,5]
|
||||
// [0,1,a,2,3,4,5]
|
||||
}
|
||||
|
||||
func ExampleTList_Back() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Back().Value)
|
||||
fmt.Println(l)
|
||||
|
||||
e := l.Back()
|
||||
l.InsertBefore(e, "a")
|
||||
l.InsertAfter(e, 6)
|
||||
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// [1,2,3,4,a,5,6]
|
||||
}
|
||||
|
||||
func ExampleTList_Len() {
|
||||
l := glist.NewTFrom[any](g.Slice{1, 2, 3, 4, 5})
|
||||
|
||||
fmt.Println(l.Len())
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
}
|
||||
|
||||
func ExampleTList_Size() {
|
||||
l := glist.NewTFrom[any](g.Slice{1, 2, 3, 4, 5})
|
||||
|
||||
fmt.Println(l.Size())
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
}
|
||||
|
||||
func ExampleTList_MoveBefore() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// element of `l`
|
||||
e := l.PushBack(6)
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
l.MoveBefore(e, l.Front())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// not element of `l`
|
||||
e = &glist.TElement[any]{Value: 7}
|
||||
l.MoveBefore(e, l.Front())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 6
|
||||
// [1,2,3,4,5,6]
|
||||
// 6
|
||||
// [6,1,2,3,4,5]
|
||||
// 6
|
||||
// [6,1,2,3,4,5]
|
||||
}
|
||||
|
||||
func ExampleTList_MoveAfter() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// element of `l`
|
||||
e := l.PushFront(0)
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
l.MoveAfter(e, l.Back())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// not element of `l`
|
||||
e = &glist.TElement[any]{Value: -1}
|
||||
l.MoveAfter(e, l.Back())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 6
|
||||
// [0,1,2,3,4,5]
|
||||
// 6
|
||||
// [1,2,3,4,5,0]
|
||||
// 6
|
||||
// [1,2,3,4,5,0]
|
||||
}
|
||||
|
||||
func ExampleTList_MoveToFront() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// element of `l`
|
||||
l.MoveToFront(l.Back())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// not element of `l`
|
||||
e := &glist.TElement[any]{Value: 6}
|
||||
l.MoveToFront(e)
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 5
|
||||
// [5,1,2,3,4]
|
||||
// 5
|
||||
// [5,1,2,3,4]
|
||||
}
|
||||
|
||||
func ExampleTList_MoveToBack() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// element of `l`
|
||||
l.MoveToBack(l.Front())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// not element of `l`
|
||||
e := &glist.TElement[any]{Value: 0}
|
||||
l.MoveToBack(e)
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 5
|
||||
// [2,3,4,5,1]
|
||||
// 5
|
||||
// [2,3,4,5,1]
|
||||
}
|
||||
|
||||
func ExampleTList_PushBackList() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
other := glist.NewTFrom[any](g.Slice{6, 7, 8, 9, 10})
|
||||
|
||||
fmt.Println(other.Size())
|
||||
fmt.Println(other)
|
||||
|
||||
l.PushBackList(other)
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 5
|
||||
// [6,7,8,9,10]
|
||||
// 10
|
||||
// [1,2,3,4,5,6,7,8,9,10]
|
||||
}
|
||||
|
||||
func ExampleTList_PushFrontList() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
other := glist.NewTFrom[any](g.Slice{-4, -3, -2, -1, 0})
|
||||
|
||||
fmt.Println(other.Size())
|
||||
fmt.Println(other)
|
||||
|
||||
l.PushFrontList(other)
|
||||
|
||||
fmt.Println(l.Size())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 5
|
||||
// [-4,-3,-2,-1,0]
|
||||
// 10
|
||||
// [-4,-3,-2,-1,0,1,2,3,4,5]
|
||||
}
|
||||
|
||||
func ExampleTList_InsertAfter() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.InsertAfter(l.Front(), "a")
|
||||
l.InsertAfter(l.Back(), "b")
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 7
|
||||
// [1,a,2,3,4,5,b]
|
||||
}
|
||||
|
||||
func ExampleTList_InsertBefore() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.InsertBefore(l.Front(), "a")
|
||||
l.InsertBefore(l.Back(), "b")
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 7
|
||||
// [a,1,2,3,4,b,5]
|
||||
}
|
||||
|
||||
func ExampleTList_Remove() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
fmt.Println(l.Remove(l.Front()))
|
||||
fmt.Println(l.Remove(l.Back()))
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 1
|
||||
// 5
|
||||
// 3
|
||||
// [2,3,4]
|
||||
}
|
||||
|
||||
func ExampleTList_Removes() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.Removes([]*glist.TElement[any]{l.Front(), l.Back()})
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 3
|
||||
// [2,3,4]
|
||||
}
|
||||
|
||||
func ExampleTList_RemoveAll() {
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice())
|
||||
|
||||
fmt.Println(l.Len())
|
||||
fmt.Println(l)
|
||||
|
||||
l.RemoveAll()
|
||||
|
||||
fmt.Println(l.Len())
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// [1,2,3,4,5]
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleTList_RLockFunc() {
|
||||
// concurrent-safe list.
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 10, 1).Slice(), true)
|
||||
// iterate reading from head.
|
||||
l.RLockFunc(func(list *list.List) {
|
||||
length := list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, list.Front(); i < length; i, e = i+1, e.Next() {
|
||||
fmt.Print(e.Value)
|
||||
}
|
||||
}
|
||||
})
|
||||
fmt.Println()
|
||||
// iterate reading from tail.
|
||||
l.RLockFunc(func(list *list.List) {
|
||||
length := list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, list.Back(); i < length; i, e = i+1, e.Prev() {
|
||||
fmt.Print(e.Value)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
fmt.Println()
|
||||
// Output:
|
||||
// 12345678910
|
||||
// 10987654321
|
||||
}
|
||||
|
||||
func ExampleTList_IteratorAsc() {
|
||||
// concurrent-safe list.
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 10, 1).Slice(), true)
|
||||
// iterate reading from head using IteratorAsc.
|
||||
l.IteratorAsc(func(e *glist.TElement[any]) bool {
|
||||
fmt.Print(e.Value)
|
||||
return true
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 12345678910
|
||||
}
|
||||
|
||||
func ExampleTList_IteratorDesc() {
|
||||
// concurrent-safe list.
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 10, 1).Slice(), true)
|
||||
// iterate reading from tail using IteratorDesc.
|
||||
l.IteratorDesc(func(e *glist.TElement[any]) bool {
|
||||
fmt.Print(e.Value)
|
||||
return true
|
||||
})
|
||||
// Output:
|
||||
// 10987654321
|
||||
}
|
||||
|
||||
func ExampleTList_LockFunc() {
|
||||
// concurrent-safe list.
|
||||
l := glist.NewTFrom[any](garray.NewArrayRange(1, 10, 1).Slice(), true)
|
||||
// iterate writing from head.
|
||||
l.LockFunc(func(list *list.List) {
|
||||
length := list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, list.Front(); i < length; i, e = i+1, e.Next() {
|
||||
if e.Value == 6 {
|
||||
e.Value = "M"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
fmt.Println(l)
|
||||
|
||||
// Output:
|
||||
// [1,2,3,4,5,M,7,8,9,10]
|
||||
}
|
||||
|
||||
func ExampleTList_Join() {
|
||||
var l glist.TList[any]
|
||||
l.PushBacks(g.Slice{"a", "b", "c", "d"})
|
||||
|
||||
fmt.Println(l.Join(","))
|
||||
|
||||
// Output:
|
||||
// a,b,c,d
|
||||
}
|
||||
933
container/glist/glist_z_unit_t_test.go
Normal file
933
container/glist/glist_z_unit_t_test.go
Normal file
@ -0,0 +1,933 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package glist
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func checkTListLen(t *gtest.T, l *TList[any], len int) bool {
|
||||
if n := l.Len(); n != len {
|
||||
t.Errorf("l.Len() = %d, want %d", n, len)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func checkTListPointers(t *gtest.T, l *TList[any], es []*TElement[any]) {
|
||||
if !checkTListLen(t, l, len(es)) {
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
l.Iterator(func(e *TElement[any]) bool {
|
||||
if e.Prev() != es[i].Prev() {
|
||||
t.Errorf("list[%d].Prev = %p, want %p", i, e.Prev(), es[i].Prev())
|
||||
return false
|
||||
}
|
||||
if e.Next() != es[i].Next() {
|
||||
t.Errorf("list[%d].Next = %p, want %p", i, e.Next(), es[i].Next())
|
||||
return false
|
||||
}
|
||||
i++
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func TestTVar(t *testing.T) {
|
||||
var l TList[any]
|
||||
l.PushFront(1)
|
||||
l.PushFront(2)
|
||||
if v := l.PopBack(); v != 1 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 1, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopBack(); v != 2 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 2, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopBack(); v != nil {
|
||||
t.Errorf("EXPECT %v, GOT %v", nil, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
if v := l.PopFront(); v != 1 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 1, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopFront(); v != 2 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 2, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopFront(); v != nil {
|
||||
t.Errorf("EXPECT %v, GOT %v", nil, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTBasic(t *testing.T) {
|
||||
l := NewT[any]()
|
||||
l.PushFront(1)
|
||||
l.PushFront(2)
|
||||
if v := l.PopBack(); v != 1 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 1, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopBack(); v != 2 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 2, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopBack(); v != nil {
|
||||
t.Errorf("EXPECT %v, GOT %v", nil, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
if v := l.PopFront(); v != 1 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 1, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopFront(); v != 2 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 2, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
if v := l.PopFront(); v != nil {
|
||||
t.Errorf("EXPECT %v, GOT %v", nil, v)
|
||||
} else {
|
||||
// fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTList(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
checkTListPointers(t, l, []*TElement[any]{})
|
||||
|
||||
// Single element list
|
||||
e := l.PushFront("a")
|
||||
checkTListPointers(t, l, []*TElement[any]{e})
|
||||
l.MoveToFront(e)
|
||||
checkTListPointers(t, l, []*TElement[any]{e})
|
||||
l.MoveToBack(e)
|
||||
checkTListPointers(t, l, []*TElement[any]{e})
|
||||
l.Remove(e)
|
||||
checkTListPointers(t, l, []*TElement[any]{})
|
||||
|
||||
// Bigger list
|
||||
e2 := l.PushFront(2)
|
||||
e1 := l.PushFront(1)
|
||||
e3 := l.PushBack(3)
|
||||
e4 := l.PushBack("banana")
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e2, e3, e4})
|
||||
|
||||
l.Remove(e2)
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e3, e4})
|
||||
|
||||
l.MoveToFront(e3) // move from middle
|
||||
checkTListPointers(t, l, []*TElement[any]{e3, e1, e4})
|
||||
|
||||
l.MoveToFront(e1)
|
||||
l.MoveToBack(e3) // move from middle
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e4, e3})
|
||||
|
||||
l.MoveToFront(e3) // move from back
|
||||
checkTListPointers(t, l, []*TElement[any]{e3, e1, e4})
|
||||
l.MoveToFront(e3) // should be no-op
|
||||
checkTListPointers(t, l, []*TElement[any]{e3, e1, e4})
|
||||
|
||||
l.MoveToBack(e3) // move from front
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e4, e3})
|
||||
l.MoveToBack(e3) // should be no-op
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e4, e3})
|
||||
|
||||
e2 = l.InsertBefore(e1, 2) // insert before front
|
||||
checkTListPointers(t, l, []*TElement[any]{e2, e1, e4, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertBefore(e4, 2) // insert before middle
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e2, e4, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertBefore(e3, 2) // insert before back
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e4, e2, e3})
|
||||
l.Remove(e2)
|
||||
|
||||
e2 = l.InsertAfter(e1, 2) // insert after front
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e2, e4, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertAfter(e4, 2) // insert after middle
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e4, e2, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertAfter(e3, 2) // insert after back
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e4, e3, e2})
|
||||
l.Remove(e2)
|
||||
|
||||
// Check standard iteration.
|
||||
sum := 0
|
||||
for e := l.Front(); e != nil; e = e.Next() {
|
||||
if i, ok := e.Value.(int); ok {
|
||||
sum += i
|
||||
}
|
||||
}
|
||||
if sum != 4 {
|
||||
t.Errorf("sum over l = %d, want 4", sum)
|
||||
}
|
||||
|
||||
// Clear all elements by iterating
|
||||
var next *TElement[any]
|
||||
for e := l.Front(); e != nil; e = next {
|
||||
next = e.Next()
|
||||
l.Remove(e)
|
||||
}
|
||||
checkTListPointers(t, l, []*TElement[any]{})
|
||||
})
|
||||
}
|
||||
|
||||
func checkTList(t *gtest.T, l *TList[any], es []any) {
|
||||
if !checkTListLen(t, l, len(es)) {
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
for e := l.Front(); e != nil; e = e.Next() {
|
||||
|
||||
switch e.Value.(type) {
|
||||
case int:
|
||||
if le := e.Value.(int); le != es[i] {
|
||||
t.Errorf("elt[%d].Value() = %v, want %v", i, le, es[i])
|
||||
}
|
||||
// default string
|
||||
default:
|
||||
if le := e.Value.(string); le != es[i] {
|
||||
t.Errorf("elt[%v].Value() = %v, want %v", i, le, es[i])
|
||||
}
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
// for e := l.Front(); e != nil; e = e.Next() {
|
||||
// le := e.Value.(int)
|
||||
// if le != es[i] {
|
||||
// t.Errorf("elt[%d].Value() = %v, want %v", i, le, es[i])
|
||||
// }
|
||||
// i++
|
||||
// }
|
||||
}
|
||||
|
||||
func TestTExtending(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l1 := NewT[any]()
|
||||
l2 := NewT[any]()
|
||||
|
||||
l1.PushBack(1)
|
||||
l1.PushBack(2)
|
||||
l1.PushBack(3)
|
||||
|
||||
l2.PushBack(4)
|
||||
l2.PushBack(5)
|
||||
|
||||
l3 := NewT[any]()
|
||||
l3.PushBackList(l1)
|
||||
checkTList(t, l3, []any{1, 2, 3})
|
||||
l3.PushBackList(l2)
|
||||
checkTList(t, l3, []any{1, 2, 3, 4, 5})
|
||||
|
||||
l3 = NewT[any]()
|
||||
l3.PushFrontList(l2)
|
||||
checkTList(t, l3, []any{4, 5})
|
||||
l3.PushFrontList(l1)
|
||||
checkTList(t, l3, []any{1, 2, 3, 4, 5})
|
||||
|
||||
checkTList(t, l1, []any{1, 2, 3})
|
||||
checkTList(t, l2, []any{4, 5})
|
||||
|
||||
l3 = NewT[any]()
|
||||
l3.PushBackList(l1)
|
||||
checkTList(t, l3, []any{1, 2, 3})
|
||||
l3.PushBackList(l3)
|
||||
checkTList(t, l3, []any{1, 2, 3, 1, 2, 3})
|
||||
|
||||
l3 = NewT[any]()
|
||||
l3.PushFrontList(l1)
|
||||
checkTList(t, l3, []any{1, 2, 3})
|
||||
l3.PushFrontList(l3)
|
||||
checkTList(t, l3, []any{1, 2, 3, 1, 2, 3})
|
||||
|
||||
l3 = NewT[any]()
|
||||
l1.PushBackList(l3)
|
||||
checkTList(t, l1, []any{1, 2, 3})
|
||||
l1.PushFrontList(l3)
|
||||
checkTList(t, l1, []any{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTRemove(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
e1 := l.PushBack(1)
|
||||
e2 := l.PushBack(2)
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e2})
|
||||
// e := l.Front()
|
||||
// l.Remove(e)
|
||||
// checkTListPointers(t, l, []*TElement[any]{e2})
|
||||
// l.Remove(e)
|
||||
// checkTListPointers(t, l, []*TElement[any]{e2})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_T_Issue4103(t *testing.T) {
|
||||
l1 := NewT[any]()
|
||||
l1.PushBack(1)
|
||||
l1.PushBack(2)
|
||||
|
||||
l2 := NewT[any]()
|
||||
l2.PushBack(3)
|
||||
l2.PushBack(4)
|
||||
|
||||
e := l1.Front()
|
||||
l2.Remove(e) // l2 should not change because e is not an element of l2
|
||||
if n := l2.Len(); n != 2 {
|
||||
t.Errorf("l2.Len() = %d, want 2", n)
|
||||
}
|
||||
|
||||
l1.InsertBefore(e, 8)
|
||||
if n := l1.Len(); n != 3 {
|
||||
t.Errorf("l1.Len() = %d, want 3", n)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_T_Issue6349(t *testing.T) {
|
||||
l := NewT[any]()
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
|
||||
e := l.Front()
|
||||
l.Remove(e)
|
||||
if e.Value != 1 {
|
||||
t.Errorf("e.value = %d, want 1", e.Value)
|
||||
}
|
||||
// if e.Next() != nil {
|
||||
// t.Errorf("e.Next() != nil")
|
||||
// }
|
||||
// if e.Prev() != nil {
|
||||
// t.Errorf("e.Prev() != nil")
|
||||
// }
|
||||
}
|
||||
|
||||
func TestTMove(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
e1 := l.PushBack(1)
|
||||
e2 := l.PushBack(2)
|
||||
e3 := l.PushBack(3)
|
||||
e4 := l.PushBack(4)
|
||||
|
||||
l.MoveAfter(e3, e3)
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e2, e3, e4})
|
||||
l.MoveBefore(e2, e2)
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e2, e3, e4})
|
||||
|
||||
l.MoveAfter(e3, e2)
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e2, e3, e4})
|
||||
l.MoveBefore(e2, e3)
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e2, e3, e4})
|
||||
|
||||
l.MoveBefore(e2, e4)
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e3, e2, e4})
|
||||
e2, e3 = e3, e2
|
||||
|
||||
l.MoveBefore(e4, e1)
|
||||
checkTListPointers(t, l, []*TElement[any]{e4, e1, e2, e3})
|
||||
e1, e2, e3, e4 = e4, e1, e2, e3
|
||||
|
||||
l.MoveAfter(e4, e1)
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e4, e2, e3})
|
||||
e2, e3, e4 = e4, e2, e3
|
||||
|
||||
l.MoveAfter(e2, e3)
|
||||
checkTListPointers(t, l, []*TElement[any]{e1, e3, e2, e4})
|
||||
e2, e3 = e3, e2
|
||||
})
|
||||
}
|
||||
|
||||
// Test PushFront, PushBack, PushFrontList, PushBackList with uninitialized List
|
||||
func TestTZeroList(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var l1 = NewT[any]()
|
||||
l1.PushFront(1)
|
||||
checkTList(t, l1, []any{1})
|
||||
|
||||
var l2 = NewT[any]()
|
||||
l2.PushBack(1)
|
||||
checkTList(t, l2, []any{1})
|
||||
|
||||
var l3 = NewT[any]()
|
||||
l3.PushFrontList(l1)
|
||||
checkTList(t, l3, []any{1})
|
||||
|
||||
var l4 = NewT[any]()
|
||||
l4.PushBackList(l2)
|
||||
checkTList(t, l4, []any{1})
|
||||
})
|
||||
}
|
||||
|
||||
// Test that a list l is not modified when calling InsertBefore with a mark that is not an element of l.
|
||||
func TestTInsertBeforeUnknownMark(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
l.PushBack(3)
|
||||
l.InsertBefore(new(TElement[any]), 1)
|
||||
checkTList(t, l, []any{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
// Test that a list l is not modified when calling InsertAfter with a mark that is not an element of l.
|
||||
func TestTInsertAfterUnknownMark(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
l.PushBack(3)
|
||||
l.InsertAfter(new(TElement[any]), 1)
|
||||
checkTList(t, l, []any{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
// Test that a list l is not modified when calling MoveAfter or MoveBefore with a mark that is not an element of l.
|
||||
func TestTMoveUnknownMark(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l1 := NewT[any]()
|
||||
e1 := l1.PushBack(1)
|
||||
|
||||
l2 := NewT[any]()
|
||||
e2 := l2.PushBack(2)
|
||||
|
||||
l1.MoveAfter(e1, e2)
|
||||
checkTList(t, l1, []any{1})
|
||||
checkTList(t, l2, []any{2})
|
||||
|
||||
l1.MoveBefore(e1, e2)
|
||||
checkTList(t, l1, []any{1})
|
||||
checkTList(t, l2, []any{2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_RemoveAll(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
l.PushBack(1)
|
||||
l.RemoveAll()
|
||||
checkTList(t, l, []any{})
|
||||
l.PushBack(2)
|
||||
checkTList(t, l, []any{2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_PushFronts(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
a1 := []any{1, 2}
|
||||
l.PushFronts(a1)
|
||||
checkTList(t, l, []any{2, 1})
|
||||
a1 = []any{3, 4, 5}
|
||||
l.PushFronts(a1)
|
||||
checkTList(t, l, []any{5, 4, 3, 2, 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_PushBacks(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
a1 := []any{1, 2}
|
||||
l.PushBacks(a1)
|
||||
checkTList(t, l, []any{1, 2})
|
||||
a1 = []any{3, 4, 5}
|
||||
l.PushBacks(a1)
|
||||
checkTList(t, l, []any{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_PopBacks(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
a1 := []any{1, 2, 3, 4}
|
||||
a2 := []any{"a", "c", "b", "e"}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopBacks(2)
|
||||
t.Assert(i1, []any{1, 2})
|
||||
|
||||
l.PushBacks(a2) // 4.3,a,c,b,e
|
||||
i1 = l.PopBacks(3)
|
||||
t.Assert(i1, []any{"e", "b", "c"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_PopFronts(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
a1 := []any{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopFronts(2)
|
||||
t.Assert(i1, []any{4, 3})
|
||||
t.Assert(l.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_PopBackAll(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
a1 := []any{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopBackAll()
|
||||
t.Assert(i1, []any{1, 2, 3, 4})
|
||||
t.Assert(l.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_PopFrontAll(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
a1 := []any{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopFrontAll()
|
||||
t.Assert(i1, []any{4, 3, 2, 1})
|
||||
t.Assert(l.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_FrontAll(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
a1 := []any{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.FrontAll()
|
||||
t.Assert(i1, []any{4, 3, 2, 1})
|
||||
t.Assert(l.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_BackAll(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
a1 := []any{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.BackAll()
|
||||
t.Assert(i1, []any{1, 2, 3, 4})
|
||||
t.Assert(l.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_FrontValue(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
l2 := NewT[any]()
|
||||
a1 := []any{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.FrontValue()
|
||||
t.Assert(gconv.Int(i1), 4)
|
||||
t.Assert(l.Len(), 4)
|
||||
|
||||
i1 = l2.FrontValue()
|
||||
t.Assert(i1, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_BackValue(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
l2 := NewT[any]()
|
||||
a1 := []any{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.BackValue()
|
||||
t.Assert(gconv.Int(i1), 1)
|
||||
t.Assert(l.Len(), 4)
|
||||
|
||||
i1 = l2.FrontValue()
|
||||
t.Assert(i1, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_Back(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
a1 := []any{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
t.Assert(e1.Value, 1)
|
||||
t.Assert(l.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_Size(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
a1 := []any{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
t.Assert(l.Size(), 4)
|
||||
l.PopFront()
|
||||
t.Assert(l.Size(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_Removes(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
a1 := []any{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
l.Removes([]*TElement[any]{e1})
|
||||
t.Assert(l.Len(), 3)
|
||||
|
||||
e2 := l.Back()
|
||||
l.Removes([]*TElement[any]{e2})
|
||||
t.Assert(l.Len(), 2)
|
||||
checkTList(t, l, []any{4, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_Pop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewTFrom([]any{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
t.Assert(l.PopBack(), 9)
|
||||
t.Assert(l.PopBacks(2), []any{8, 7})
|
||||
t.Assert(l.PopFront(), 1)
|
||||
t.Assert(l.PopFronts(2), []any{2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_Clear(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
a1 := []any{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
l.Clear()
|
||||
t.Assert(l.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_IteratorAsc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
a1 := []any{1, 2, 5, 6, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
fun1 := func(e *TElement[any]) bool {
|
||||
return gconv.Int(e1.Value) > 2
|
||||
}
|
||||
checkTList(t, l, []any{4, 3, 6, 5, 2, 1})
|
||||
l.IteratorAsc(fun1)
|
||||
checkTList(t, l, []any{4, 3, 6, 5, 2, 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_IteratorDesc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
a1 := []any{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
fun1 := func(e *TElement[any]) bool {
|
||||
return gconv.Int(e1.Value) > 6
|
||||
}
|
||||
l.IteratorDesc(fun1)
|
||||
t.Assert(l.Len(), 4)
|
||||
checkTList(t, l, []any{4, 3, 2, 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_Iterator(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
a1 := []any{"a", "b", "c", "d", "e"}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
fun1 := func(e *TElement[any]) bool {
|
||||
return gconv.String(e1.Value) > "c"
|
||||
}
|
||||
checkTList(t, l, []any{"e", "d", "c", "b", "a"})
|
||||
l.Iterator(fun1)
|
||||
checkTList(t, l, []any{"e", "d", "c", "b", "a"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_Join(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewTFrom([]any{1, 2, "a", `"b"`, `\c`})
|
||||
t.Assert(l.Join(","), `1,2,a,"b",\c`)
|
||||
t.Assert(l.Join("."), `1.2.a."b".\c`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_String(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewTFrom([]any{1, 2, "a", `"b"`, `\c`})
|
||||
t.Assert(l.String(), `[1,2,a,"b",\c]`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_Json(t *testing.T) {
|
||||
// Marshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := []any{"a", "b", "c"}
|
||||
l := NewT[any]()
|
||||
l.PushBacks(a)
|
||||
b1, err1 := json.Marshal(l)
|
||||
b2, err2 := json.Marshal(a)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
// Unmarshal
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := []any{"a", "b", "c"}
|
||||
l := NewT[any]()
|
||||
b, err := json.Marshal(a)
|
||||
t.AssertNil(err)
|
||||
|
||||
err = json.UnmarshalUseNumber(b, l)
|
||||
t.AssertNil(err)
|
||||
t.Assert(l.FrontAll(), a)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var l TList[any]
|
||||
a := []any{"a", "b", "c"}
|
||||
b, err := json.Marshal(a)
|
||||
t.AssertNil(err)
|
||||
|
||||
err = json.UnmarshalUseNumber(b, &l)
|
||||
t.AssertNil(err)
|
||||
t.Assert(l.FrontAll(), a)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_UnmarshalValue(t *testing.T) {
|
||||
type list struct {
|
||||
Name string
|
||||
List *TList[any]
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tlist *list
|
||||
err := gconv.Struct(map[string]any{
|
||||
"name": "john",
|
||||
"list": []byte(`[1,2,3]`),
|
||||
}, &tlist)
|
||||
t.AssertNil(err)
|
||||
t.Assert(tlist.Name, "john")
|
||||
t.Assert(tlist.List.FrontAll(), []any{1, 2, 3})
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tlist *list
|
||||
err := gconv.Struct(map[string]any{
|
||||
"name": "john",
|
||||
"list": []any{1, 2, 3},
|
||||
}, &tlist)
|
||||
t.AssertNil(err)
|
||||
t.Assert(tlist.Name, "john")
|
||||
t.Assert(tlist.List.FrontAll(), []any{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_DeepCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewTFrom([]any{1, 2, "a", `"b"`, `\c`})
|
||||
copyList := l.DeepCopy()
|
||||
cl := copyList.(*TList[any])
|
||||
cl.PopBack()
|
||||
t.AssertNE(l.Size(), cl.Size())
|
||||
})
|
||||
// Nil pointer deep copy
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var l *TList[any]
|
||||
copyList := l.DeepCopy()
|
||||
t.AssertNil(copyList)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_ToList(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewTFrom([]any{1, 2, 3, 4, 5})
|
||||
nl := l.ToList()
|
||||
t.Assert(nl.Len(), 5)
|
||||
|
||||
// Verify elements
|
||||
i := 1
|
||||
for e := nl.Front(); e != nil; e = e.Next() {
|
||||
t.Assert(e.Value, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
// Empty list
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
nl := l.ToList()
|
||||
t.Assert(nl.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_AppendList(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewTFrom([]any{1, 2, 3})
|
||||
nl := list.New()
|
||||
nl.PushBack(4)
|
||||
nl.PushBack(5)
|
||||
|
||||
l.AppendList(nl)
|
||||
t.Assert(l.Len(), 5)
|
||||
t.Assert(l.FrontAll(), []any{1, 2, 3, 4, 5})
|
||||
})
|
||||
// Append empty list
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewTFrom([]any{1, 2, 3})
|
||||
nl := list.New()
|
||||
l.AppendList(nl)
|
||||
t.Assert(l.Len(), 3)
|
||||
t.Assert(l.FrontAll(), []any{1, 2, 3})
|
||||
})
|
||||
// Append with type mismatch (should skip)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[int]()
|
||||
nl := list.New()
|
||||
nl.PushBack(1)
|
||||
nl.PushBack("string") // type mismatch
|
||||
nl.PushBack(2)
|
||||
|
||||
l.AppendList(nl)
|
||||
t.Assert(l.Len(), 2)
|
||||
t.Assert(l.FrontAll(), []int{1, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_AssignList(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewTFrom([]any{1, 2, 3})
|
||||
nl := list.New()
|
||||
nl.PushBack(4)
|
||||
nl.PushBack(5)
|
||||
nl.PushBack(6)
|
||||
|
||||
skipped := l.AssignList(nl)
|
||||
t.Assert(skipped, 0)
|
||||
t.Assert(l.Len(), 3)
|
||||
t.Assert(l.FrontAll(), []any{4, 5, 6})
|
||||
})
|
||||
// Assign empty list
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewTFrom([]any{1, 2, 3})
|
||||
nl := list.New()
|
||||
|
||||
skipped := l.AssignList(nl)
|
||||
t.Assert(skipped, 0)
|
||||
t.Assert(l.Len(), 0)
|
||||
})
|
||||
// Assign with type mismatch (should return skipped count)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[int]()
|
||||
nl := list.New()
|
||||
nl.PushBack(1)
|
||||
nl.PushBack("string") // type mismatch
|
||||
nl.PushBack(2)
|
||||
nl.PushBack("another") // type mismatch
|
||||
|
||||
skipped := l.AssignList(nl)
|
||||
t.Assert(skipped, 2)
|
||||
t.Assert(l.Len(), 2)
|
||||
t.Assert(l.FrontAll(), []int{1, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_String_Nil(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var l *TList[any]
|
||||
t.Assert(l.String(), "")
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_UnmarshalJSON_Error(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
err := l.UnmarshalJSON([]byte("invalid json"))
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_UnmarshalValue_String(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
err := l.UnmarshalValue(`[1,2,3]`)
|
||||
t.AssertNil(err)
|
||||
t.Assert(l.FrontAll(), []any{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_UnmarshalValue_Bytes(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
err := l.UnmarshalValue([]byte(`[1,2,3]`))
|
||||
t.AssertNil(err)
|
||||
t.Assert(l.FrontAll(), []any{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_DeepCopy_Empty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
copyList := l.DeepCopy()
|
||||
cl := copyList.(*TList[any])
|
||||
t.Assert(cl.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_AppendList_WithTypeMismatch(t *testing.T) {
|
||||
// Test appendList internal function through AppendList with mixed types
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[int]()
|
||||
nl := list.New()
|
||||
// Only add non-matching types
|
||||
nl.PushBack("string1")
|
||||
nl.PushBack("string2")
|
||||
|
||||
l.AppendList(nl)
|
||||
t.Assert(l.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTList_UnmarshalValue_Error(t *testing.T) {
|
||||
// Test UnmarshalValue with data through default case
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
// Pass a slice directly through default case
|
||||
_ = l.UnmarshalValue([]any{1, 2, 3})
|
||||
t.Assert(l.Len(), 3)
|
||||
t.Assert(l.FrontAll(), []any{1, 2, 3})
|
||||
})
|
||||
// Test UnmarshalValue error in string case
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewT[any]()
|
||||
err := l.UnmarshalValue("invalid json")
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user