mirror of
https://github.com/charlienet/go-mixed.git
synced 2025-07-17 16:12:42 +08:00
list
This commit is contained in:
180
collections/list/array_list.go
Normal file
180
collections/list/array_list.go
Normal file
@ -0,0 +1,180 @@
|
||||
package list
|
||||
|
||||
import (
|
||||
"github.com/charlienet/go-mixed/locker"
|
||||
)
|
||||
|
||||
const minCapacity = 16
|
||||
|
||||
type ArrayList[T any] struct {
|
||||
buf []T
|
||||
head int
|
||||
tail int
|
||||
minCap int
|
||||
list[T]
|
||||
}
|
||||
|
||||
func NewArrayList[T any](elems ...T) *ArrayList[T] {
|
||||
minCap := minCapacity
|
||||
var buf []T
|
||||
|
||||
return &ArrayList[T]{
|
||||
buf: buf,
|
||||
minCap: minCap,
|
||||
list: list[T]{locker: locker.EmptyLocker},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ArrayList[T]) PushFront(v T) {
|
||||
l.locker.Lock()
|
||||
defer l.locker.Unlock()
|
||||
|
||||
l.grow()
|
||||
|
||||
l.head = l.prev(l.head)
|
||||
l.buf[l.head] = v
|
||||
l.size++
|
||||
}
|
||||
|
||||
func (l *ArrayList[T]) PushBack(v T) {
|
||||
l.locker.Lock()
|
||||
defer l.locker.Unlock()
|
||||
|
||||
l.grow()
|
||||
|
||||
l.buf[l.tail] = v
|
||||
|
||||
l.tail = l.next(l.tail)
|
||||
l.size++
|
||||
}
|
||||
|
||||
func (l *ArrayList[T]) PopFront() T {
|
||||
l.locker.Lock()
|
||||
defer l.locker.Unlock()
|
||||
|
||||
if l.size <= 0 {
|
||||
panic("list: PopFront() called on empty list")
|
||||
}
|
||||
ret := l.buf[l.head]
|
||||
var zero T
|
||||
l.buf[l.head] = zero
|
||||
|
||||
l.head = l.next(l.head)
|
||||
l.size--
|
||||
|
||||
l.shrink()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (l *ArrayList[T]) PopBack() T {
|
||||
l.locker.Lock()
|
||||
defer l.locker.Unlock()
|
||||
|
||||
l.tail = l.prev(l.tail)
|
||||
|
||||
ret := l.buf[l.tail]
|
||||
var zero T
|
||||
l.buf[l.tail] = zero
|
||||
l.size--
|
||||
|
||||
l.shrink()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (l *ArrayList[T]) RemoveAt(at int) T {
|
||||
if at < 0 || at >= l.Size() {
|
||||
panic(ErrorOutOffRange)
|
||||
}
|
||||
|
||||
l.locker.Lock()
|
||||
defer l.locker.Unlock()
|
||||
|
||||
rm := (l.head + at) & (len(l.buf) - 1)
|
||||
if at*2 < l.size {
|
||||
for i := 0; i < at; i++ {
|
||||
prev := l.prev(rm)
|
||||
l.buf[prev], l.buf[rm] = l.buf[rm], l.buf[prev]
|
||||
rm = prev
|
||||
}
|
||||
return l.PopFront()
|
||||
}
|
||||
swaps := l.size - at - 1
|
||||
for i := 0; i < swaps; i++ {
|
||||
next := l.next(rm)
|
||||
l.buf[rm], l.buf[next] = l.buf[next], l.buf[rm]
|
||||
rm = next
|
||||
}
|
||||
return l.PopBack()
|
||||
}
|
||||
|
||||
func (l *ArrayList[T]) Front() T {
|
||||
l.locker.RLock()
|
||||
defer l.locker.RUnlock()
|
||||
|
||||
return l.buf[l.head]
|
||||
}
|
||||
|
||||
func (l *ArrayList[T]) Back() T {
|
||||
l.locker.RLock()
|
||||
defer l.locker.RUnlock()
|
||||
|
||||
return l.buf[l.tail]
|
||||
}
|
||||
|
||||
func (l *ArrayList[T]) ForEach(fn func(T)) {
|
||||
l.locker.RLock()
|
||||
defer l.locker.RUnlock()
|
||||
|
||||
n := l.head
|
||||
for i := 0; i < l.size; i++ {
|
||||
fn(l.buf[n])
|
||||
|
||||
n = l.next(n)
|
||||
}
|
||||
}
|
||||
|
||||
func (q *ArrayList[T]) prev(i int) int {
|
||||
return (i - 1) & (len(q.buf) - 1)
|
||||
}
|
||||
|
||||
func (l *ArrayList[T]) next(i int) int {
|
||||
return (i + 1) & (len(l.buf) - 1)
|
||||
}
|
||||
|
||||
func (l *ArrayList[T]) grow() {
|
||||
if l.size != len(l.buf) {
|
||||
return
|
||||
}
|
||||
if len(l.buf) == 0 {
|
||||
if l.minCap == 0 {
|
||||
l.minCap = minCapacity
|
||||
}
|
||||
l.buf = make([]T, l.minCap)
|
||||
return
|
||||
}
|
||||
|
||||
l.resize()
|
||||
}
|
||||
|
||||
func (l *ArrayList[T]) shrink() {
|
||||
if len(l.buf) > l.minCap && (l.size<<2) == len(l.buf) {
|
||||
l.resize()
|
||||
}
|
||||
}
|
||||
|
||||
// resize resizes the list to fit exactly twice its current contents. This is
|
||||
// used to grow the list when it is full, and also to shrink it when it is
|
||||
// only a quarter full.
|
||||
func (l *ArrayList[T]) resize() {
|
||||
newBuf := make([]T, l.size<<1)
|
||||
if l.tail > l.head {
|
||||
copy(newBuf, l.buf[l.head:l.tail])
|
||||
} else {
|
||||
n := copy(newBuf, l.buf[l.head:])
|
||||
copy(newBuf[n:], l.buf[:l.tail])
|
||||
}
|
||||
|
||||
l.head = 0
|
||||
l.tail = l.size
|
||||
l.buf = newBuf
|
||||
}
|
40
collections/list/array_list_test.go
Normal file
40
collections/list/array_list_test.go
Normal file
@ -0,0 +1,40 @@
|
||||
package list_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/charlienet/go-mixed/collections/list"
|
||||
)
|
||||
|
||||
func TestNewArrayList(t *testing.T) {
|
||||
l := list.NewArrayList[int]()
|
||||
|
||||
_ = l
|
||||
}
|
||||
|
||||
func TestArrayPushBack(t *testing.T) {
|
||||
l := list.NewArrayList[int]()
|
||||
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
l.PushBack(3)
|
||||
|
||||
l.ForEach(func(i int) {
|
||||
t.Log(i)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArrayPushFront(t *testing.T) {
|
||||
l := list.NewArrayList[int]()
|
||||
|
||||
l.PushFront(1)
|
||||
l.PushFront(2)
|
||||
l.PushFront(3)
|
||||
|
||||
l.PushBack(99)
|
||||
l.PushBack(88)
|
||||
|
||||
l.ForEach(func(i int) {
|
||||
t.Log(i)
|
||||
})
|
||||
}
|
141
collections/list/linked_list.go
Normal file
141
collections/list/linked_list.go
Normal file
@ -0,0 +1,141 @@
|
||||
package list
|
||||
|
||||
import (
|
||||
"github.com/charlienet/go-mixed/locker"
|
||||
)
|
||||
|
||||
type LinkedList[T any] struct {
|
||||
list[T]
|
||||
front, tail *LinkedNode[T]
|
||||
}
|
||||
|
||||
type LinkedNode[T any] struct {
|
||||
Value T
|
||||
Prev, Next *LinkedNode[T]
|
||||
}
|
||||
|
||||
func NewLinkedList[T any](elems ...T) *LinkedList[T] {
|
||||
return &LinkedList[T]{
|
||||
list: list[T]{locker: locker.EmptyLocker},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LinkedList[T]) PushBack(v T) *LinkedList[T] {
|
||||
l.pushBackNode(&LinkedNode[T]{Value: v})
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *LinkedList[T]) PushFront(v T) *LinkedList[T] {
|
||||
l.pushFrontNode(&LinkedNode[T]{Value: v})
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *LinkedList[T]) FrontNode() *LinkedNode[T] {
|
||||
return l.front
|
||||
}
|
||||
|
||||
func (l *LinkedList[T]) Front() T {
|
||||
return l.FrontNode().Value
|
||||
}
|
||||
|
||||
func (l *LinkedList[T]) BackNode() *LinkedNode[T] {
|
||||
return l.tail
|
||||
}
|
||||
|
||||
func (l *LinkedList[T]) Back() T {
|
||||
if l.size == 0 {
|
||||
panic(ErrorOutOffRange)
|
||||
}
|
||||
return l.tail.Value
|
||||
}
|
||||
|
||||
func (l *LinkedList[T]) ForEach(fn func(T) bool) {
|
||||
l.locker.RLock()
|
||||
defer l.locker.RUnlock()
|
||||
|
||||
for current := l.front; current != nil; current = current.Next {
|
||||
if fn(current.Value) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LinkedList[T]) Remove(n *LinkedNode[T]) {
|
||||
l.locker.Lock()
|
||||
defer l.locker.Unlock()
|
||||
|
||||
if n.Next != nil {
|
||||
n.Next.Prev = n.Prev
|
||||
} else {
|
||||
l.tail = n.Prev
|
||||
}
|
||||
|
||||
if n.Prev != nil {
|
||||
n.Prev.Next = n.Next
|
||||
} else {
|
||||
l.front = n.Next
|
||||
}
|
||||
|
||||
l.size--
|
||||
}
|
||||
|
||||
func (l *LinkedList[T]) RemoveAt(index int) {
|
||||
l.locker.Lock()
|
||||
defer l.locker.Unlock()
|
||||
|
||||
var i int
|
||||
var prev *LinkedNode[T]
|
||||
|
||||
prev = l.front
|
||||
for current := l.front; current != nil; {
|
||||
|
||||
if i == index {
|
||||
prev.Next = current.Next
|
||||
current.Next = nil
|
||||
|
||||
l.size--
|
||||
return
|
||||
}
|
||||
|
||||
prev = current
|
||||
current = current.Next
|
||||
i++
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (l *LinkedList[T]) pushBackNode(n *LinkedNode[T]) {
|
||||
l.locker.Lock()
|
||||
defer l.locker.Unlock()
|
||||
|
||||
n.Next = nil
|
||||
n.Prev = l.tail
|
||||
|
||||
if l.tail != nil {
|
||||
l.tail.Next = n
|
||||
} else {
|
||||
l.front = n
|
||||
}
|
||||
|
||||
l.tail = n
|
||||
|
||||
l.size++
|
||||
}
|
||||
|
||||
func (l *LinkedList[T]) pushFrontNode(n *LinkedNode[T]) {
|
||||
l.locker.Lock()
|
||||
defer l.locker.Unlock()
|
||||
|
||||
n.Next = l.front
|
||||
n.Prev = nil
|
||||
if l.front != nil {
|
||||
l.front.Prev = n
|
||||
} else {
|
||||
l.tail = n
|
||||
}
|
||||
l.front = n
|
||||
|
||||
l.size++
|
||||
}
|
87
collections/list/linked_list_test.go
Normal file
87
collections/list/linked_list_test.go
Normal file
@ -0,0 +1,87 @@
|
||||
package list_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/charlienet/go-mixed/collections/list"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPushBack(t *testing.T) {
|
||||
l := list.NewLinkedList[int]()
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
l.PushBack(3)
|
||||
|
||||
l.ForEach(func(i int) bool {
|
||||
t.Log(i)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func TestPushFront(t *testing.T) {
|
||||
l := list.NewLinkedList[int]()
|
||||
l.PushFront(1)
|
||||
l.PushFront(2)
|
||||
l.PushFront(3)
|
||||
|
||||
l.ForEach(func(i int) bool {
|
||||
t.Log(i)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func TestRemoveAt(t *testing.T) {
|
||||
|
||||
l := list.NewLinkedList[int]()
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
l.PushBack(3)
|
||||
|
||||
l.RemoveAt(1)
|
||||
|
||||
l.ForEach(func(i int) bool {
|
||||
t.Log(i)
|
||||
return false
|
||||
})
|
||||
|
||||
t.Log()
|
||||
|
||||
l.RemoveAt(0)
|
||||
l.ForEach(func(i int) bool {
|
||||
t.Log(i)
|
||||
return false
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSize(t *testing.T) {
|
||||
l := list.NewLinkedList[int]()
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
l.PushBack(3)
|
||||
|
||||
assert.Equal(t, 3, l.Size())
|
||||
|
||||
l.RemoveAt(0)
|
||||
assert.Equal(t, 2, l.Size())
|
||||
}
|
||||
|
||||
func TestLinkedListToSlice(t *testing.T) {
|
||||
l := list.NewLinkedList[int]()
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
l.PushBack(3)
|
||||
|
||||
s := l.ToSlice()
|
||||
t.Log(s)
|
||||
}
|
||||
|
||||
func BenchmarkLinkedList(b *testing.B) {
|
||||
l := list.NewLinkedList[int]()
|
||||
l.Synchronize()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
l.PushBack(i)
|
||||
}
|
||||
}
|
35
collections/list/list.go
Normal file
35
collections/list/list.go
Normal file
@ -0,0 +1,35 @@
|
||||
package list
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/charlienet/go-mixed/locker"
|
||||
)
|
||||
|
||||
var ErrorOutOffRange = errors.New("out of range")
|
||||
|
||||
type List[T any] interface {
|
||||
}
|
||||
|
||||
type list[T any] struct {
|
||||
size int
|
||||
locker locker.RWLocker
|
||||
}
|
||||
|
||||
func (l *list[T]) Synchronize() {
|
||||
l.locker = locker.NewRWLocker()
|
||||
}
|
||||
|
||||
func (l *list[T]) ForEach(fn func(T) bool) { panic("Not Implemented") }
|
||||
|
||||
func (l *LinkedList[T]) ToSlice() []T {
|
||||
s := make([]T, 0, l.Size())
|
||||
l.ForEach(func(t T) bool {
|
||||
s = append(s, t)
|
||||
return false
|
||||
})
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (l *list[T]) Size() int { return l.size }
|
@ -7,17 +7,15 @@ import (
|
||||
|
||||
// MaxInt returns the larger one of v1 and v2.
|
||||
func Max[T constraints.Ordered](v1, v2 T) T {
|
||||
return expr.If(v1 > v2, v1, v2)
|
||||
return expr.Ternary(v1 > v2, v1, v2)
|
||||
}
|
||||
|
||||
// MinInt returns the smaller one of v1 and v2.
|
||||
func Min[T constraints.Ordered](v1, v2 T) T {
|
||||
return expr.If(v1 < v2, v1, v2)
|
||||
return expr.Ternary(v1 < v2, v1, v2)
|
||||
}
|
||||
|
||||
|
||||
|
||||
func abs(n int64) int64 {
|
||||
func Abs(n int64) int64 {
|
||||
y := n >> 63
|
||||
return (n ^ y) - y
|
||||
}
|
||||
|
Reference in New Issue
Block a user