mirror of
https://gitee.com/johng/gf
synced 2026-06-09 11:03:59 +08:00
Compare commits
117 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 75725db6fb | |||
| 5cd8475143 | |||
| 5629f37939 | |||
| 08ec04d8b6 | |||
| c0b46f364a | |||
| 8c5f74e8bb | |||
| 94832262e3 | |||
| aefbfd52e9 | |||
| f3f0689bd4 | |||
| 5198d4c5fc | |||
| 123f2d3e4e | |||
| 3c750c3c92 | |||
| 17b29cd19f | |||
| cf1077bec4 | |||
| 4e2e4e95e0 | |||
| 61d64e7ae4 | |||
| 883797c495 | |||
| 0113971877 | |||
| 664b0c06a6 | |||
| bd4c75a98e | |||
| d35840409b | |||
| abaef9ba87 | |||
| b15d8bdd2e | |||
| 718997327a | |||
| fdfefbb94d | |||
| 2b865a55ac | |||
| 8138215597 | |||
| 7cc0c7a1cc | |||
| 4c647aaa19 | |||
| 48b1d616c5 | |||
| 693c37d6d6 | |||
| d525c04826 | |||
| c170edbdfc | |||
| 66e40155a9 | |||
| 59ad1a9b00 | |||
| a5b536e218 | |||
| 0e6c2e790d | |||
| 5761e73061 | |||
| 34c761e9db | |||
| 87e3813636 | |||
| 361ff0315c | |||
| 2bb227d058 | |||
| 99dc69e839 | |||
| d78fde8099 | |||
| 5d0c8956d6 | |||
| c9537af062 | |||
| dfb5b3a8ce | |||
| a177e44583 | |||
| 7ae03729f3 | |||
| 898ec21a25 | |||
| 6d7d8dec02 | |||
| ea7e2ec5ec | |||
| a5b8e2aa2f | |||
| 123333d9c2 | |||
| 0c4fa1d96a | |||
| e5805e8c69 | |||
| bf2d45a012 | |||
| a7122788b1 | |||
| 237c58f2b0 | |||
| efa23e4a1d | |||
| c109cee7ef | |||
| e111d39c54 | |||
| 66306464e1 | |||
| dd34ac1722 | |||
| 34cb222b33 | |||
| a0276f7e81 | |||
| d39ef156de | |||
| f464dc7fb8 | |||
| d29b27a5df | |||
| 5346ca9046 | |||
| 6d5b552bb7 | |||
| aadc6aa504 | |||
| e8c3dfa13e | |||
| 836d62f4aa | |||
| 9eea93cc6e | |||
| 308cb55b6b | |||
| 75ada78f8f | |||
| ecd86e3a12 | |||
| c1aa5eb717 | |||
| d2fed1198b | |||
| a9f9261dbd | |||
| 161e0d6e97 | |||
| 3efe511f42 | |||
| 5d04c2e50a | |||
| 7b26b7ea4c | |||
| 9d1063c6b2 | |||
| 5ff7632d32 | |||
| e6fb41504c | |||
| a800f731dd | |||
| f1a9fbb74e | |||
| cf81a73526 | |||
| 65036fffe8 | |||
| a69934a7e3 | |||
| 9400457bf2 | |||
| 5060329721 | |||
| 07ab1d60e8 | |||
| 7377a82e19 | |||
| f82e3ac808 | |||
| 1a6cd1de04 | |||
| 90e6f685b7 | |||
| 0fc825dac1 | |||
| dbb27efe3e | |||
| 2d3d2e783e | |||
| 6ae1defa35 | |||
| 16a4a5ba46 | |||
| 8300885ab6 | |||
| b489eed4ef | |||
| 053a3c1a53 | |||
| 468c315087 | |||
| b3d5fc149e | |||
| 43886511b9 | |||
| fd63a2209b | |||
| a26ec37f59 | |||
| 779ad93bcb | |||
| 1ec0219473 | |||
| 388d5954cb | |||
| 20977558cc |
@ -76,6 +76,7 @@ func main() {
|
||||
- [pibigstar](https://github.com/pibigstar)
|
||||
- [qq1054000800](https://gitee.com/qq1054000800)
|
||||
- [qq976739120](https://github.com/qq976739120)
|
||||
- [touzijiao](https://github.com/touzijiao)
|
||||
- [wenzi1](https://gitee.com/wenzi1)
|
||||
- [wxkj001](https://github.com/wxkj001)
|
||||
- [ymrjqyy](https://gitee.com/ymrjqyy)
|
||||
|
||||
@ -94,6 +94,7 @@ func main() {
|
||||
- [pibigstar](https://github.com/pibigstar)
|
||||
- [qq1054000800](https://gitee.com/qq1054000800)
|
||||
- [qq976739120](https://github.com/qq976739120)
|
||||
- [touzijiao](https://github.com/touzijiao)
|
||||
- [wenzi1](https://gitee.com/wenzi1)
|
||||
- [wxkj001](https://github.com/wxkj001)
|
||||
- [ymrjqyy](https://gitee.com/ymrjqyy)
|
||||
|
||||
1
TODO.MD
1
TODO.MD
@ -45,6 +45,7 @@
|
||||
1. gredis增加cluster支持;
|
||||
1. gset.Add/Remove/Contains方法增加批量操作支持;
|
||||
1. gmlock增加手动清理机制:当内存锁不再使用时,由调用端决定是否清理内存锁;
|
||||
1. gtimer增加DelayAdd*方法返回Entry对象,以便DelayAdd*的定时任务也能进行状态控制;gcron同理需要改进;
|
||||
|
||||
# DONE
|
||||
1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换;
|
||||
|
||||
@ -8,7 +8,8 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
@ -16,24 +17,20 @@ import (
|
||||
)
|
||||
|
||||
type IntArray struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
array []int // 底层数组
|
||||
mu *rwmutex.RWMutex
|
||||
array []int
|
||||
}
|
||||
|
||||
// Create an empty array.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewIntArray creates and returns an empty array.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntArray(unsafe...bool) *IntArray {
|
||||
return NewIntArraySize(0, 0, unsafe...)
|
||||
}
|
||||
|
||||
// Create an array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个指定大小的数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewIntArraySize create and returns an array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntArraySize(size int, cap int, unsafe...bool) *IntArray {
|
||||
return &IntArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
@ -41,11 +38,9 @@ func NewIntArraySize(size int, cap int, unsafe...bool) *IntArray {
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice变量创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewIntArrayFrom creates and returns an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntArrayFrom(array []int, unsafe...bool) *IntArray {
|
||||
return &IntArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
@ -53,11 +48,9 @@ func NewIntArrayFrom(array []int, unsafe...bool) *IntArray {
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice拷贝创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewIntArrayFromCopy creates and returns an array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntArrayFromCopy(array []int, unsafe...bool) *IntArray {
|
||||
newArray := make([]int, len(array))
|
||||
copy(newArray, array)
|
||||
@ -67,9 +60,8 @@ func NewIntArrayFromCopy(array []int, unsafe...bool) *IntArray {
|
||||
}
|
||||
}
|
||||
|
||||
// Get value by index.
|
||||
//
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界。
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *IntArray) Get(index int) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -77,9 +69,7 @@ func (a *IntArray) Get(index int) int {
|
||||
return value
|
||||
}
|
||||
|
||||
// Set value by index.
|
||||
//
|
||||
// 设置指定索引的数据项, 调用方注意判断数组边界。
|
||||
// Set sets value to specified index.
|
||||
func (a *IntArray) Set(index int, value int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -87,9 +77,7 @@ func (a *IntArray) Set(index int, value int) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Set the underlying slice array with the given <array> param.
|
||||
//
|
||||
// 设置底层数组变量.
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
func (a *IntArray) SetArray(array []int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -97,9 +85,7 @@ func (a *IntArray) SetArray(array []int) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Replace the array items by given <array> from the beginning of array.
|
||||
//
|
||||
// 使用指定数组替换到对应的索引元素值.
|
||||
// Replace replaces the array items by given <array> from the beginning of array.
|
||||
func (a *IntArray) Replace(array []int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -113,9 +99,7 @@ func (a *IntArray) Replace(array []int) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Calculate the sum of values in an array.
|
||||
//
|
||||
// 对数组中的元素项求和。
|
||||
// Sum returns the sum of values in an array.
|
||||
func (a *IntArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -125,11 +109,9 @@ func (a *IntArray) Sum() (sum int) {
|
||||
return
|
||||
}
|
||||
|
||||
// Sort the array in increasing order.
|
||||
// Sort sorts the array in increasing order.
|
||||
// The param <reverse> controls whether sort
|
||||
// in increasing order(default) or decreasing order
|
||||
//
|
||||
// 将数组排序(默认从低到高).
|
||||
func (a *IntArray) Sort(reverse...bool) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -146,9 +128,7 @@ func (a *IntArray) Sort(reverse...bool) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Sort the array by custom function <less>.
|
||||
//
|
||||
// 使用自定义的排序函数将数组重新排序.
|
||||
// SortFunc sorts the array by custom function <less>.
|
||||
func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -158,9 +138,7 @@ func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Insert the <value> to the front of <index>.
|
||||
//
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
|
||||
// InsertBefore inserts the <value> to the front of <index>.
|
||||
func (a *IntArray) InsertBefore(index int, value int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -170,9 +148,7 @@ func (a *IntArray) InsertBefore(index int, value int) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Insert the <value> to the back of <index>.
|
||||
//
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
func (a *IntArray) InsertAfter(index int, value int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -182,13 +158,11 @@ func (a *IntArray) InsertAfter(index int, value int) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Remove an item by index.
|
||||
//
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界。
|
||||
// Remove removes an item by index.
|
||||
func (a *IntArray) Remove(index int) int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// 边界删除判断,以提高删除效率
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
@ -198,15 +172,15 @@ func (a *IntArray) Remove(index int) int {
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
return value
|
||||
}
|
||||
|
||||
// Push new items to the beginning of array.
|
||||
//
|
||||
// 将数据项添加到数组的最左端(索引为0)。
|
||||
// PushLeft pushes one or multiple items to the beginning of array.
|
||||
func (a *IntArray) PushLeft(value...int) *IntArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(value, a.array...)
|
||||
@ -214,9 +188,8 @@ func (a *IntArray) PushLeft(value...int) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Push new items to the end of array.
|
||||
//
|
||||
// 将数据项添加到数组的最右端(索引为length - 1), 等于: Append。
|
||||
// PushRight pushes one or multiple items to the end of array.
|
||||
// It equals to Append.
|
||||
func (a *IntArray) PushRight(value...int) *IntArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
@ -224,9 +197,7 @@ func (a *IntArray) PushRight(value...int) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Pop an item from the beginning of array.
|
||||
//
|
||||
// 将最左端(索引为0)的数据项移出数组,并返回该数据项。
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
func (a *IntArray) PopLeft() int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -235,9 +206,7 @@ func (a *IntArray) PopLeft() int {
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop an item from the end of array.
|
||||
//
|
||||
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项。
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
func (a *IntArray) PopRight() int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -247,16 +216,12 @@ func (a *IntArray) PopRight() int {
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
func (a *IntArray) PopRand() int {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
func (a *IntArray) PopRands(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -272,9 +237,7 @@ func (a *IntArray) PopRands(size int) []int {
|
||||
return array
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项。
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
func (a *IntArray) PopLefts(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -287,9 +250,7 @@ func (a *IntArray) PopLefts(size int) []int {
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop <size> items from the end of array.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
func (a *IntArray) PopRights(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -302,11 +263,9 @@ func (a *IntArray) PopRights(size int) []int {
|
||||
return value
|
||||
}
|
||||
|
||||
// Get items by range, returns array[start:end].
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *IntArray) Range(start, end int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -333,8 +292,6 @@ func (a *IntArray) Range(start, end int) []int {
|
||||
}
|
||||
|
||||
// See PushRight.
|
||||
//
|
||||
// 追加数据项, 等于: PushRight。
|
||||
func (a *IntArray) Append(value...int) *IntArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
@ -342,9 +299,7 @@ func (a *IntArray) Append(value...int) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Get the length of array.
|
||||
//
|
||||
// 数组长度。
|
||||
// Len returns the length of array.
|
||||
func (a *IntArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
@ -352,11 +307,9 @@ func (a *IntArray) Len() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// Get the underlying data of array.
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// Slice returns the underlying data of array.
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 返回原始数据数组.
|
||||
func (a *IntArray) Slice() []int {
|
||||
array := ([]int)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
@ -370,9 +323,7 @@ func (a *IntArray) Slice() []int {
|
||||
return array
|
||||
}
|
||||
|
||||
// Return a new array, which is a copy of current array.
|
||||
//
|
||||
// 克隆当前数组,返回当前数组的一个拷贝。
|
||||
// Clone returns a new array, which is a copy of current array.
|
||||
func (a *IntArray) Clone() (newArray *IntArray) {
|
||||
a.mu.RLock()
|
||||
array := make([]int, len(a.array))
|
||||
@ -381,9 +332,7 @@ func (a *IntArray) Clone() (newArray *IntArray) {
|
||||
return NewIntArrayFrom(array, !a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear array.
|
||||
//
|
||||
// 清空数据数组。
|
||||
// Clear deletes all items of current array.
|
||||
func (a *IntArray) Clear() *IntArray {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
@ -393,17 +342,13 @@ func (a *IntArray) Clear() *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Check whether a value exists in the array.
|
||||
//
|
||||
// 查找指定数值是否存在。
|
||||
// Contains checks whether a value exists in the array.
|
||||
func (a *IntArray) Contains(value int) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
|
||||
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
|
||||
//
|
||||
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *IntArray) Search(value int) int {
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
@ -421,9 +366,7 @@ func (a *IntArray) Search(value int) int {
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique the array, clear repeated values.
|
||||
//
|
||||
// 清理数组中重复的元素项。
|
||||
// Unique uniques the array, clear repeated items.
|
||||
func (a *IntArray) Unique() *IntArray {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array) - 1; i++ {
|
||||
@ -437,9 +380,7 @@ func (a *IntArray) Unique() *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
// LockFunc locks writing by callback function <f>.
|
||||
func (a *IntArray) LockFunc(f func(array []int)) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -447,9 +388,7 @@ func (a *IntArray) LockFunc(f func(array []int)) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
// RLockFunc locks reading by callback function <f>.
|
||||
func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -457,11 +396,10 @@ func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays. The parameter <array> can be any garray type or slice type.
|
||||
// Merge merges <array> into current array.
|
||||
// The parameter <array> can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
// but Merge supports more parameter types.
|
||||
func (a *IntArray) Merge(array interface{}) *IntArray {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Append(gconv.Ints(v.Slice())...)
|
||||
@ -476,10 +414,8 @@ func (a *IntArray) Merge(array interface{}) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Fills an array with num entries of the value of the value parameter,
|
||||
// keys starting at the startIndex parameter.
|
||||
//
|
||||
// 用value参数的值将数组填充num个条目,位置由startIndex参数指定的开始。
|
||||
// Fill fills an array with num entries of the value <value>,
|
||||
// keys starting at the <startIndex> parameter.
|
||||
func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -496,10 +432,9 @@ func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Chunks an array into arrays with size elements.
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// The last chunk may contain less than size elements.
|
||||
//
|
||||
// 将一个数组分割成多个数组,其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
|
||||
func (a *IntArray) Chunk(size int) [][]int {
|
||||
if size < 1 {
|
||||
return nil
|
||||
@ -520,14 +455,10 @@ func (a *IntArray) Chunk(size int) [][]int {
|
||||
return n
|
||||
}
|
||||
|
||||
// Pad array to the specified length with a value.
|
||||
// Pad pads array to the specified length with <value>.
|
||||
// If size is positive then the array is padded on the right, or negative on the left.
|
||||
// If the absolute value of size is less than or equal to the length of the array
|
||||
// If the absolute value of <size> is less than or equal to the length of the array
|
||||
// then no padding takes place.
|
||||
//
|
||||
// 返回数组的一个拷贝,并用value将其填补到size指定的长度。
|
||||
// 如果size为正数,则填补到数组的右侧,如果为负数则从左侧开始填补。
|
||||
// 如果size的绝对值小于或等于数组的长度则没有任何填补。
|
||||
func (a *IntArray) Pad(size int, value int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -551,12 +482,9 @@ func (a *IntArray) Pad(size int, value int) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Extract a slice of the array(If in concurrent safe usage,
|
||||
// it returns a copy of the slice; else a pointer).
|
||||
// It returns the sequence of elements from the array array as specified
|
||||
// by the offset and length parameters.
|
||||
//
|
||||
// 返回根据offset和size参数所指定的数组中的一段序列。
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
func (a *IntArray) SubSlice(offset, size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -575,18 +503,14 @@ func (a *IntArray) SubSlice(offset, size int) []int {
|
||||
}
|
||||
}
|
||||
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *IntArray) Rand() int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
func (a *IntArray) Rands(size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -603,9 +527,7 @@ func (a *IntArray) Rands(size int) []int {
|
||||
return n
|
||||
}
|
||||
|
||||
// Randomly shuffles the array.
|
||||
//
|
||||
// 随机打乱当前数组。
|
||||
// Shuffle randomly shuffles the array.
|
||||
func (a *IntArray) Shuffle() *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -615,9 +537,7 @@ func (a *IntArray) Shuffle() *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Make array with elements in reverse order.
|
||||
//
|
||||
// 将当前数组反转。
|
||||
// Reverse makes array with elements in reverse order.
|
||||
func (a *IntArray) Reverse() *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -627,9 +547,7 @@ func (a *IntArray) Reverse() *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Join array elements with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前数组的元素项,构造成新的字符串返回。
|
||||
// Join joins array elements with a string <glue>.
|
||||
func (a *IntArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -641,4 +559,22 @@ func (a *IntArray) Join(glue string) string {
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// CountValues counts the number of occurrences of all values in the array.
|
||||
func (a *IntArray) CountValues() map[int]int {
|
||||
m := make(map[int]int)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
m[v]++
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// String returns current array as a string.
|
||||
func (a *IntArray) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return fmt.Sprint(a.array)
|
||||
}
|
||||
@ -17,15 +17,13 @@ import (
|
||||
)
|
||||
|
||||
type Array struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
array []interface{} // 底层数组
|
||||
mu *rwmutex.RWMutex
|
||||
array []interface{}
|
||||
}
|
||||
|
||||
// Create an empty array.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// New creates and returns an empty array.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func New(unsafe...bool) *Array {
|
||||
return NewArraySize(0, 0, unsafe...)
|
||||
}
|
||||
@ -35,11 +33,9 @@ func NewArray(unsafe...bool) *Array {
|
||||
return NewArraySize(0, 0, unsafe...)
|
||||
}
|
||||
|
||||
// Create an array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个指定大小的数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewArraySize create and returns an array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewArraySize(size int, cap int, unsafe...bool) *Array {
|
||||
return &Array{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
@ -57,11 +53,9 @@ func NewFromCopy(array []interface{}, unsafe...bool) *Array {
|
||||
return NewArrayFromCopy(array, unsafe...)
|
||||
}
|
||||
|
||||
// Create an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice变量创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewArrayFrom creates and returns an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewArrayFrom(array []interface{}, unsafe...bool) *Array {
|
||||
return &Array{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
@ -69,11 +63,9 @@ func NewArrayFrom(array []interface{}, unsafe...bool) *Array {
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice拷贝创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewArrayFromCopy creates and returns an array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewArrayFromCopy(array []interface{}, unsafe...bool) *Array {
|
||||
newArray := make([]interface{}, len(array))
|
||||
copy(newArray, array)
|
||||
@ -83,9 +75,8 @@ func NewArrayFromCopy(array []interface{}, unsafe...bool) *Array {
|
||||
}
|
||||
}
|
||||
|
||||
// Get value by index.
|
||||
//
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *Array) Get(index int) interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -93,9 +84,7 @@ func (a *Array) Get(index int) interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// Set value by index.
|
||||
//
|
||||
// 设置指定索引的数据项, 调用方注意判断数组边界
|
||||
// Set sets value to specified index.
|
||||
func (a *Array) Set(index int, value interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -103,9 +92,7 @@ func (a *Array) Set(index int, value interface{}) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Set the underlying slice array with the given <array> param.
|
||||
//
|
||||
// 设置底层数组变量.
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
func (a *Array) SetArray(array []interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -113,9 +100,7 @@ func (a *Array) SetArray(array []interface{}) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Replace the array items by given <array> from the beginning of array.
|
||||
//
|
||||
// 使用指定数组替换到对应的索引元素值.
|
||||
// Replace replaces the array items by given <array> from the beginning of array.
|
||||
func (a *Array) Replace(array []interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -129,9 +114,7 @@ func (a *Array) Replace(array []interface{}) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Calculate the sum of values in an array.
|
||||
//
|
||||
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
|
||||
// Sum returns the sum of values in an array.
|
||||
func (a *Array) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -141,9 +124,7 @@ func (a *Array) Sum() (sum int) {
|
||||
return
|
||||
}
|
||||
|
||||
// Sort the array by custom function <less>.
|
||||
//
|
||||
// 使用自定义的排序函数将数组重新排序.
|
||||
// SortFunc sorts the array by custom function <less>.
|
||||
func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -153,9 +134,7 @@ func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Insert the <value> to the front of <index>.
|
||||
//
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
|
||||
// InsertBefore inserts the <value> to the front of <index>.
|
||||
func (a *Array) InsertBefore(index int, value interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -165,9 +144,7 @@ func (a *Array) InsertBefore(index int, value interface{}) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Insert the <value> to the back of <index>.
|
||||
//
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
func (a *Array) InsertAfter(index int, value interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -177,13 +154,11 @@ func (a *Array) InsertAfter(index int, value interface{}) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Remove an item by index.
|
||||
//
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界。
|
||||
// Remove removes an item by index.
|
||||
func (a *Array) Remove(index int) interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// 边界删除判断,以提高删除效率
|
||||
// Determine array boundaries when deleting to improve deletion efficiency。
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
@ -193,15 +168,15 @@ func (a *Array) Remove(index int) interface{} {
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
return value
|
||||
}
|
||||
|
||||
// Push new items to the beginning of array.
|
||||
//
|
||||
// 将数据项添加到数组的最左端(索引为0)。
|
||||
// PushLeft pushes one or multiple items to the beginning of array.
|
||||
func (a *Array) PushLeft(value...interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
a.array = append(value, a.array...)
|
||||
@ -209,9 +184,8 @@ func (a *Array) PushLeft(value...interface{}) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Push new items to the end of array.
|
||||
//
|
||||
// 将数据项添加到数组的最右端(索引为length - 1), 等于: Append。
|
||||
// PushRight pushes one or multiple items to the end of array.
|
||||
// It equals to Append.
|
||||
func (a *Array) PushRight(value...interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
@ -219,16 +193,12 @@ func (a *Array) PushRight(value...interface{}) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
func (a *Array) PopRand() interface{} {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
func (a *Array) PopRands(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -244,9 +214,7 @@ func (a *Array) PopRands(size int) []interface{} {
|
||||
return array
|
||||
}
|
||||
|
||||
// Pop an item from the beginning of array.
|
||||
//
|
||||
// 将最左端(索引为0)的数据项移出数组,并返回该数据项。
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
func (a *Array) PopLeft() interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -255,9 +223,7 @@ func (a *Array) PopLeft() interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop an item from the end of array.
|
||||
//
|
||||
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项。
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
func (a *Array) PopRight() interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -267,9 +233,7 @@ func (a *Array) PopRight() interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
func (a *Array) PopLefts(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -282,9 +246,7 @@ func (a *Array) PopLefts(size int) []interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop <size> items from the end of array.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
func (a *Array) PopRights(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -297,11 +259,9 @@ func (a *Array) PopRights(size int) []interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// Get items by range, returns array[start:end].
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *Array) Range(start, end int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -328,16 +288,12 @@ func (a *Array) Range(start, end int) []interface{} {
|
||||
}
|
||||
|
||||
// See PushRight.
|
||||
//
|
||||
// 追加数据项, 等于: PushRight。
|
||||
func (a *Array) Append(value...interface{}) *Array {
|
||||
a.PushRight(value...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Get the length of array.
|
||||
//
|
||||
// 数组长度。
|
||||
// Len returns the length of array.
|
||||
func (a *Array) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
@ -345,11 +301,9 @@ func (a *Array) Len() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// Get the underlying data of array.
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// Slice returns the underlying data of array.
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 返回原始数据数组.
|
||||
func (a *Array) Slice() []interface{} {
|
||||
array := ([]interface{})(nil)
|
||||
if a.mu.IsSafe() {
|
||||
@ -363,9 +317,7 @@ func (a *Array) Slice() []interface{} {
|
||||
return array
|
||||
}
|
||||
|
||||
// Return a new array, which is a copy of current array.
|
||||
//
|
||||
// 克隆当前数组,返回当前数组的一个拷贝。
|
||||
// Clone returns a new array, which is a copy of current array.
|
||||
func (a *Array) Clone() (newArray *Array) {
|
||||
a.mu.RLock()
|
||||
array := make([]interface{}, len(a.array))
|
||||
@ -374,9 +326,7 @@ func (a *Array) Clone() (newArray *Array) {
|
||||
return NewArrayFrom(array, !a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear array.
|
||||
//
|
||||
// 清空数据数组
|
||||
// Clear deletes all items of current array.
|
||||
func (a *Array) Clear() *Array {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
@ -386,16 +336,13 @@ func (a *Array) Clear() *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Check whether a value exists in the array.
|
||||
//
|
||||
// 查找指定数值是否存在
|
||||
// Contains checks whether a value exists in the array.
|
||||
func (a *Array) Contains(value interface{}) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
|
||||
//
|
||||
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *Array) Search(value interface{}) int {
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
@ -413,9 +360,7 @@ func (a *Array) Search(value interface{}) int {
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique the array, clear repeated values.
|
||||
//
|
||||
// 清理数组中重复的元素项
|
||||
// Unique uniques the array, clear repeated items.
|
||||
func (a *Array) Unique() *Array {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array) - 1; i++ {
|
||||
@ -429,9 +374,7 @@ func (a *Array) Unique() *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作
|
||||
// LockFunc locks writing by callback function <f>.
|
||||
func (a *Array) LockFunc(f func(array []interface{})) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -439,9 +382,7 @@ func (a *Array) LockFunc(f func(array []interface{})) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作
|
||||
// RLockFunc locks reading by callback function <f>.
|
||||
func (a *Array) RLockFunc(f func(array []interface{})) *Array {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -449,11 +390,10 @@ func (a *Array) RLockFunc(f func(array []interface{})) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays. The parameter <array> can be any garray type or slice type.
|
||||
// Merge merges <array> into current array.
|
||||
// The parameter <array> can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
// but Merge supports more parameter types.
|
||||
func (a *Array) Merge(array interface{}) *Array {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Append(gconv.Interfaces(v.Slice())...)
|
||||
@ -468,10 +408,8 @@ func (a *Array) Merge(array interface{}) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Fills an array with num entries of the value of the value parameter,
|
||||
// keys starting at the start_index parameter.
|
||||
//
|
||||
// 用value参数的值将数组填充num个条目,位置由startIndex参数指定的开始。
|
||||
// Fill fills an array with num entries of the value <value>,
|
||||
// keys starting at the <startIndex> parameter.
|
||||
func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -488,10 +426,9 @@ func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Chunks an array into arrays with size elements.
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// The last chunk may contain less than size elements.
|
||||
//
|
||||
// 将一个数组分割成多个数组,其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
|
||||
func (a *Array) Chunk(size int) [][]interface{} {
|
||||
if size < 1 {
|
||||
return nil
|
||||
@ -512,15 +449,10 @@ func (a *Array) Chunk(size int) [][]interface{} {
|
||||
return n
|
||||
}
|
||||
|
||||
// Pad array to the specified length with a value.
|
||||
// If size is positive then the array is padded on the right,
|
||||
// if it's negative then on the left.
|
||||
// If the absolute value of size is less than or equal to the length of the array
|
||||
// Pad pads array to the specified length with <value>.
|
||||
// If size is positive then the array is padded on the right, or negative on the left.
|
||||
// If the absolute value of <size> is less than or equal to the length of the array
|
||||
// then no padding takes place.
|
||||
//
|
||||
// 返回数组的一个拷贝,并用value将其填补到size指定的长度。
|
||||
// 如果size为正数,则填补到数组的右侧,如果为负数则从左侧开始填补。
|
||||
// 如果size的绝对值小于或等于数组的长度则没有任何填补。
|
||||
func (a *Array) Pad(size int, val interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -544,10 +476,9 @@ func (a *Array) Pad(size int, val interface{}) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Extract a slice of the array(If in concurrent safe usage, it returns a copy of the slice; else a pointer).
|
||||
// It returns the sequence of elements from the array array as specified by the offset and length parameters.
|
||||
//
|
||||
// 返回根据offset和size参数所指定的数组中的一段序列。
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
func (a *Array) SubSlice(offset, size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -566,18 +497,14 @@ func (a *Array) SubSlice(offset, size int) []interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *Array) Rand() interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
func (a *Array) Rands(size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -594,9 +521,7 @@ func (a *Array) Rands(size int) []interface{} {
|
||||
return n
|
||||
}
|
||||
|
||||
// Randomly shuffles the array.
|
||||
//
|
||||
// 随机打乱当前数组。
|
||||
// Shuffle randomly shuffles the array.
|
||||
func (a *Array) Shuffle() *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -606,9 +531,7 @@ func (a *Array) Shuffle() *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Make array with elements in reverse order.
|
||||
//
|
||||
// 将当前数组反转。
|
||||
// Reverse makes array with elements in reverse order.
|
||||
func (a *Array) Reverse() *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -618,9 +541,7 @@ func (a *Array) Reverse() *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Join array elements with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前数组的元素项,构造成新的字符串返回。
|
||||
// Join joins array elements with a string <glue>.
|
||||
func (a *Array) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -634,9 +555,7 @@ func (a *Array) Join(glue string) string {
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// Counts all the values of an array.
|
||||
//
|
||||
// 统计数组中所有的值出现的次数.
|
||||
// CountValues counts the number of occurrences of all values in the array.
|
||||
func (a *Array) CountValues() map[interface{}]int {
|
||||
m := make(map[interface{}]int)
|
||||
a.mu.RLock()
|
||||
@ -648,8 +567,6 @@ func (a *Array) CountValues() map[interface{}]int {
|
||||
}
|
||||
|
||||
// String returns current array as a string.
|
||||
//
|
||||
// 将当前数组转换为字符串返回。
|
||||
func (a *Array) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
|
||||
@ -8,7 +8,8 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
@ -17,24 +18,20 @@ import (
|
||||
)
|
||||
|
||||
type StringArray struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
array []string // 底层数组
|
||||
mu *rwmutex.RWMutex
|
||||
array []string
|
||||
}
|
||||
|
||||
// Create an empty array.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewStringArray creates and returns an empty array.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStringArray(unsafe...bool) *StringArray {
|
||||
return NewStringArraySize(0, 0, unsafe...)
|
||||
}
|
||||
|
||||
// Create an array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个指定大小的数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewStringArraySize create and returns an array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStringArraySize(size int, cap int, unsafe...bool) *StringArray {
|
||||
return &StringArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
@ -42,11 +39,9 @@ func NewStringArraySize(size int, cap int, unsafe...bool) *StringArray {
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice变量创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewStringArrayFrom creates and returns an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStringArrayFrom(array []string, unsafe...bool) *StringArray {
|
||||
return &StringArray {
|
||||
mu : rwmutex.New(unsafe...),
|
||||
@ -54,11 +49,9 @@ func NewStringArrayFrom(array []string, unsafe...bool) *StringArray {
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice拷贝创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewStringArrayFromCopy creates and returns an array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStringArrayFromCopy(array []string, unsafe...bool) *StringArray {
|
||||
newArray := make([]string, len(array))
|
||||
copy(newArray, array)
|
||||
@ -68,9 +61,8 @@ func NewStringArrayFromCopy(array []string, unsafe...bool) *StringArray {
|
||||
}
|
||||
}
|
||||
|
||||
// Get value by index.
|
||||
//
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界。
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *StringArray) Get(index int) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -78,9 +70,7 @@ func (a *StringArray) Get(index int) string {
|
||||
return value
|
||||
}
|
||||
|
||||
// Set value by index.
|
||||
//
|
||||
// 设置指定索引的数据项, 调用方注意判断数组边界。
|
||||
// Set sets value to specified index.
|
||||
func (a *StringArray) Set(index int, value string) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -88,9 +78,7 @@ func (a *StringArray) Set(index int, value string) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Set the underlying slice array with the given <array> param.
|
||||
//
|
||||
// 设置底层数组变量.
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
func (a *StringArray) SetArray(array []string) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -98,9 +86,7 @@ func (a *StringArray) SetArray(array []string) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Replace the array items by given <array> from the beginning of array.
|
||||
//
|
||||
// 使用指定数组替换到对应的索引元素值.
|
||||
// Replace replaces the array items by given <array> from the beginning of array.
|
||||
func (a *StringArray) Replace(array []string) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -114,9 +100,7 @@ func (a *StringArray) Replace(array []string) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Calculate the sum of values in an array.
|
||||
//
|
||||
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
|
||||
// Sum returns the sum of values in an array.
|
||||
func (a *StringArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -126,11 +110,9 @@ func (a *StringArray) Sum() (sum int) {
|
||||
return
|
||||
}
|
||||
|
||||
// Sort the array in increasing order.
|
||||
// Sort sorts the array in increasing order.
|
||||
// The param <reverse> controls whether sort
|
||||
// in increasing order(default) or decreasing order
|
||||
//
|
||||
// 将数组排序(默认从低到高).
|
||||
func (a *StringArray) Sort(reverse...bool) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -147,9 +129,7 @@ func (a *StringArray) Sort(reverse...bool) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Sort the array by custom function <less>.
|
||||
//
|
||||
// 使用自定义的排序函数将数组重新排序.
|
||||
// SortFunc sorts the array by custom function <less>.
|
||||
func (a *StringArray) SortFunc(less func(v1, v2 string) bool) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -159,9 +139,7 @@ func (a *StringArray) SortFunc(less func(v1, v2 string) bool) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Insert the <value> to the front of <index>.
|
||||
//
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
|
||||
// InsertBefore inserts the <value> to the front of <index>.
|
||||
func (a *StringArray) InsertBefore(index int, value string) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -171,9 +149,7 @@ func (a *StringArray) InsertBefore(index int, value string) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Insert the <value> to the back of <index>.
|
||||
//
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
func (a *StringArray) InsertAfter(index int, value string) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -183,13 +159,11 @@ func (a *StringArray) InsertAfter(index int, value string) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Remove an item by index.
|
||||
//
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界。
|
||||
// Remove removes an item by index.
|
||||
func (a *StringArray) Remove(index int) string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// 边界删除判断,以提高删除效率
|
||||
// Determine array boundaries when deleting to improve deletion efficiency。
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
@ -199,15 +173,15 @@ func (a *StringArray) Remove(index int) string {
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
return value
|
||||
}
|
||||
|
||||
// Push new items to the beginning of array.
|
||||
//
|
||||
// 将数据项添加到数组的最左端(索引为0)。
|
||||
// PushLeft pushes one or multiple items to the beginning of array.
|
||||
func (a *StringArray) PushLeft(value...string) *StringArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(value, a.array...)
|
||||
@ -215,9 +189,8 @@ func (a *StringArray) PushLeft(value...string) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Push new items to the end of array.
|
||||
//
|
||||
// 将数据项添加到数组的最右端(索引为length - 1), 等于: Append。
|
||||
// PushRight pushes one or multiple items to the end of array.
|
||||
// It equals to Append.
|
||||
func (a *StringArray) PushRight(value...string) *StringArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
@ -225,9 +198,7 @@ func (a *StringArray) PushRight(value...string) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Pop an item from the beginning of array.
|
||||
//
|
||||
// 将最左端(索引为0)的数据项移出数组,并返回该数据项。
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
func (a *StringArray) PopLeft() string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -236,9 +207,7 @@ func (a *StringArray) PopLeft() string {
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop an item from the end of array.
|
||||
//
|
||||
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项。
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
func (a *StringArray) PopRight() string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -248,16 +217,12 @@ func (a *StringArray) PopRight() string {
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
func (a *StringArray) PopRand() string {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
func (a *StringArray) PopRands(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -273,9 +238,7 @@ func (a *StringArray) PopRands(size int) []string {
|
||||
return array
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
func (a *StringArray) PopLefts(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -288,9 +251,7 @@ func (a *StringArray) PopLefts(size int) []string {
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop <size> items from the end of array.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
func (a *StringArray) PopRights(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -303,11 +264,9 @@ func (a *StringArray) PopRights(size int) []string {
|
||||
return value
|
||||
}
|
||||
|
||||
// Get items by range, returns array[start:end].
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *StringArray) Range(start, end int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -334,8 +293,6 @@ func (a *StringArray) Range(start, end int) []string {
|
||||
}
|
||||
|
||||
// See PushRight.
|
||||
//
|
||||
// 追加数据项, 等于: PushRight。
|
||||
func (a *StringArray) Append(value...string) *StringArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
@ -343,9 +300,7 @@ func (a *StringArray) Append(value...string) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Get the length of array.
|
||||
//
|
||||
// 数组长度。
|
||||
// Len returns the length of array.
|
||||
func (a *StringArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
@ -353,11 +308,9 @@ func (a *StringArray) Len() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// Get the underlying data of array.
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// Slice returns the underlying data of array.
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 返回原始数据数组.
|
||||
func (a *StringArray) Slice() []string {
|
||||
array := ([]string)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
@ -371,9 +324,7 @@ func (a *StringArray) Slice() []string {
|
||||
return array
|
||||
}
|
||||
|
||||
// Return a new array, which is a copy of current array.
|
||||
//
|
||||
// 克隆当前数组,返回当前数组的一个拷贝。
|
||||
// Clone returns a new array, which is a copy of current array.
|
||||
func (a *StringArray) Clone() (newArray *StringArray) {
|
||||
a.mu.RLock()
|
||||
array := make([]string, len(a.array))
|
||||
@ -382,9 +333,7 @@ func (a *StringArray) Clone() (newArray *StringArray) {
|
||||
return NewStringArrayFrom(array, !a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear array.
|
||||
//
|
||||
// 清空数据数组。
|
||||
// Clear deletes all items of current array.
|
||||
func (a *StringArray) Clear() *StringArray {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
@ -394,16 +343,13 @@ func (a *StringArray) Clear() *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Check whether a value exists in the array.
|
||||
//
|
||||
// 查找指定数值是否存在。
|
||||
// Contains checks whether a value exists in the array.
|
||||
func (a *StringArray) Contains(value string) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
|
||||
//
|
||||
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *StringArray) Search(value string) int {
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
@ -420,9 +366,7 @@ func (a *StringArray) Search(value string) int {
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique the array, clear repeated values.
|
||||
//
|
||||
// 清理数组中重复的元素项。
|
||||
// Unique uniques the array, clear repeated items.
|
||||
func (a *StringArray) Unique() *StringArray {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array) - 1; i++ {
|
||||
@ -436,9 +380,7 @@ func (a *StringArray) Unique() *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
// LockFunc locks writing by callback function <f>.
|
||||
func (a *StringArray) LockFunc(f func(array []string)) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -446,9 +388,7 @@ func (a *StringArray) LockFunc(f func(array []string)) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
// RLockFunc locks reading by callback function <f>.
|
||||
func (a *StringArray) RLockFunc(f func(array []string)) *StringArray {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -456,11 +396,10 @@ func (a *StringArray) RLockFunc(f func(array []string)) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays. The parameter <array> can be any garray type or slice type.
|
||||
// Merge merges <array> into current array.
|
||||
// The parameter <array> can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
// but Merge supports more parameter types.
|
||||
func (a *StringArray) Merge(array interface{}) *StringArray {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Append(gconv.Strings(v.Slice())...)
|
||||
@ -475,10 +414,8 @@ func (a *StringArray) Merge(array interface{}) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Fills an array with num entries of the value of the value parameter,
|
||||
// keys starting at the start_index parameter.
|
||||
//
|
||||
// 用value参数的值将数组填充num个条目,位置由startIndex参数指定的开始。
|
||||
// Fill fills an array with num entries of the value <value>,
|
||||
// keys starting at the <startIndex> parameter.
|
||||
func (a *StringArray) Fill(startIndex int, num int, value string) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -495,10 +432,9 @@ func (a *StringArray) Fill(startIndex int, num int, value string) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Chunks an array into arrays with size elements.
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// The last chunk may contain less than size elements.
|
||||
//
|
||||
// 将一个数组分割成多个数组,其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
|
||||
func (a *StringArray) Chunk(size int) [][]string {
|
||||
if size < 1 {
|
||||
return nil
|
||||
@ -519,15 +455,10 @@ func (a *StringArray) Chunk(size int) [][]string {
|
||||
return n
|
||||
}
|
||||
|
||||
// Pad array to the specified length with a value.
|
||||
// If size is positive then the array is padded on the right,
|
||||
// if it's negative then on the left.
|
||||
// If the absolute value of size is less than or equal to the length of the array
|
||||
// Pad pads array to the specified length with <value>.
|
||||
// If size is positive then the array is padded on the right, or negative on the left.
|
||||
// If the absolute value of <size> is less than or equal to the length of the array
|
||||
// then no padding takes place.
|
||||
//
|
||||
// 返回数组的一个拷贝,并用value将其填补到size指定的长度。
|
||||
// 如果size为正数,则填补到数组的右侧,如果为负数则从左侧开始填补。
|
||||
// 如果size的绝对值小于或等于数组的长度则没有任何填补。
|
||||
func (a *StringArray) Pad(size int, value string) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -551,12 +482,9 @@ func (a *StringArray) Pad(size int, value string) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Extract a slice of the array(If in concurrent safe usage,
|
||||
// it returns a copy of the slice; else a pointer).
|
||||
// It returns the sequence of elements from the array array as specified
|
||||
// by the offset and length parameters.
|
||||
//
|
||||
// 返回根据offset和size参数所指定的数组中的一段序列。
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
func (a *StringArray) SubSlice(offset, size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -575,18 +503,14 @@ func (a *StringArray) SubSlice(offset, size int) []string {
|
||||
}
|
||||
}
|
||||
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *StringArray) Rand() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
func (a *StringArray) Rands(size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -603,9 +527,7 @@ func (a *StringArray) Rands(size int) []string {
|
||||
return n
|
||||
}
|
||||
|
||||
// Randomly shuffles the array.
|
||||
//
|
||||
// 随机打乱当前数组。
|
||||
// Shuffle randomly shuffles the array.
|
||||
func (a *StringArray) Shuffle() *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -615,9 +537,7 @@ func (a *StringArray) Shuffle() *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Make array with elements in reverse order.
|
||||
//
|
||||
// 将当前数组反转。
|
||||
// Reverse makes array with elements in reverse order.
|
||||
func (a *StringArray) Reverse() *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -627,9 +547,7 @@ func (a *StringArray) Reverse() *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Join array elements with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前数组的元素项,构造成新的字符串返回。
|
||||
// Join joins array elements with a string <glue>.
|
||||
func (a *StringArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -643,3 +561,20 @@ func (a *StringArray) Join(glue string) string {
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// CountValues counts the number of occurrences of all values in the array.
|
||||
func (a *StringArray) CountValues() map[string]int {
|
||||
m := make(map[string]int)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
m[v]++
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// String returns current array as a string.
|
||||
func (a *StringArray) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return fmt.Sprint(a.array)
|
||||
}
|
||||
|
||||
@ -8,7 +8,8 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
@ -16,34 +17,30 @@ import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// 默认按照从小到大进行排序
|
||||
// It's using increasing order in default.
|
||||
type SortedIntArray struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
array []int // 底层数组
|
||||
unique *gtype.Bool // 是否要求不能重复(默认false)
|
||||
compareFunc func(v1, v2 int) int // 比较函数,返回值 -1: v1 < v2;0: v1 == v2;1: v1 > v2
|
||||
mu *rwmutex.RWMutex
|
||||
array []int
|
||||
unique *gtype.Bool // Whether enable unique feature(false)
|
||||
comparator func(v1, v2 int) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
|
||||
}
|
||||
|
||||
// Create an empty sorted array.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewSortedIntArray creates and returns an empty sorted array.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedIntArray(unsafe...bool) *SortedIntArray {
|
||||
return NewSortedIntArraySize(0, unsafe...)
|
||||
}
|
||||
|
||||
// Create a sorted array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个指定大小的排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewSortedIntArraySize create and returns an sorted array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedIntArraySize(cap int, unsafe...bool) *SortedIntArray {
|
||||
return &SortedIntArray {
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : make([]int, 0, cap),
|
||||
unique : gtype.NewBool(),
|
||||
compareFunc : func(v1, v2 int) int {
|
||||
comparator : func(v1, v2 int) int {
|
||||
if v1 < v2 {
|
||||
return -1
|
||||
}
|
||||
@ -55,11 +52,9 @@ func NewSortedIntArraySize(cap int, unsafe...bool) *SortedIntArray {
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice变量创建排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewIntArrayFrom creates and returns an sorted array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedIntArrayFrom(array []int, unsafe...bool) *SortedIntArray {
|
||||
a := NewSortedIntArraySize(0, unsafe...)
|
||||
a.array = array
|
||||
@ -67,11 +62,9 @@ func NewSortedIntArrayFrom(array []int, unsafe...bool) *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Create an array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice拷贝创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewSortedIntArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedIntArrayFromCopy(array []int, unsafe...bool) *SortedIntArray {
|
||||
newArray := make([]int, len(array))
|
||||
copy(newArray, array)
|
||||
@ -81,9 +74,7 @@ func NewSortedIntArrayFromCopy(array []int, unsafe...bool) *SortedIntArray {
|
||||
}
|
||||
}
|
||||
|
||||
// Set the underlying slice array with the given <array> param.
|
||||
//
|
||||
// 设置底层数组变量.
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -92,9 +83,9 @@ func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Sort the array in increasing order.
|
||||
//
|
||||
// 将数组排序(默认从低到高).
|
||||
// Sort sorts the array in increasing order.
|
||||
// The param <reverse> controls whether sort
|
||||
// in increasing order(default) or decreasing order.
|
||||
func (a *SortedIntArray) Sort() *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -102,9 +93,7 @@ func (a *SortedIntArray) Sort() *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// And values to sorted array, the array always keeps sorted.
|
||||
//
|
||||
// 添加数据项.
|
||||
// Add adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
func (a *SortedIntArray) Add(values...int) *SortedIntArray {
|
||||
if len(values) == 0 {
|
||||
return a
|
||||
@ -120,7 +109,6 @@ func (a *SortedIntArray) Add(values...int) *SortedIntArray {
|
||||
a.array = append(a.array, value)
|
||||
continue
|
||||
}
|
||||
// 加到指定索引后面
|
||||
if cmp > 0 {
|
||||
index++
|
||||
}
|
||||
@ -131,9 +119,8 @@ func (a *SortedIntArray) Add(values...int) *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Get value by index.
|
||||
//
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界。
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *SortedIntArray) Get(index int) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -141,13 +128,11 @@ func (a *SortedIntArray) Get(index int) int {
|
||||
return value
|
||||
}
|
||||
|
||||
// Remove an item by index.
|
||||
//
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界。
|
||||
// Remove removes an item by index.
|
||||
func (a *SortedIntArray) Remove(index int) int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// 边界删除判断,以提高删除效率
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
@ -157,15 +142,15 @@ func (a *SortedIntArray) Remove(index int) int {
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
return value
|
||||
}
|
||||
|
||||
// Push new items to the beginning of array.
|
||||
//
|
||||
// 将数据项添加到数组的最左端(索引为0)。
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
func (a *SortedIntArray) PopLeft() int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -174,9 +159,7 @@ func (a *SortedIntArray) PopLeft() int {
|
||||
return value
|
||||
}
|
||||
|
||||
// Push new items to the end of array.
|
||||
//
|
||||
// 将数据项添加到数组的最右端(索引为length - 1)。
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
func (a *SortedIntArray) PopRight() int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -186,16 +169,12 @@ func (a *SortedIntArray) PopRight() int {
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
func (a *SortedIntArray) PopRand() int {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
func (a *SortedIntArray) PopRands(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -211,9 +190,7 @@ func (a *SortedIntArray) PopRands(size int) []int {
|
||||
return array
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
func (a *SortedIntArray) PopLefts(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -226,9 +203,7 @@ func (a *SortedIntArray) PopLefts(size int) []int {
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop <size> items from the end of array.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
func (a *SortedIntArray) PopRights(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -241,11 +216,9 @@ func (a *SortedIntArray) PopRights(size int) []int {
|
||||
return value
|
||||
}
|
||||
|
||||
// Get items by range, returns array[start:end].
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *SortedIntArray) Range(start, end int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -271,9 +244,7 @@ func (a *SortedIntArray) Range(start, end int) []int {
|
||||
return array
|
||||
}
|
||||
|
||||
// Get the length of array.
|
||||
//
|
||||
// 数组长度。
|
||||
// Len returns the length of array.
|
||||
func (a *SortedIntArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
@ -281,9 +252,7 @@ func (a *SortedIntArray) Len() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// Calculate the sum of values in an array.
|
||||
//
|
||||
// 对数组中的元素项求和。
|
||||
// Sum returns the sum of values in an array.
|
||||
func (a *SortedIntArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -293,11 +262,9 @@ func (a *SortedIntArray) Sum() (sum int) {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the underlying data of array.
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// Slice returns the underlying data of array.
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 返回原始数据数组.
|
||||
func (a *SortedIntArray) Slice() []int {
|
||||
array := ([]int)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
@ -311,24 +278,19 @@ func (a *SortedIntArray) Slice() []int {
|
||||
return array
|
||||
}
|
||||
|
||||
// Check whether a value exists in the array.
|
||||
//
|
||||
// 查找指定数值是否存在。
|
||||
// Contains checks whether a value exists in the array.
|
||||
func (a *SortedIntArray) Contains(value int) bool {
|
||||
return a.Search(value) == 0
|
||||
}
|
||||
|
||||
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
|
||||
//
|
||||
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *SortedIntArray) Search(value int) (index int) {
|
||||
index, _ = a.binSearch(value, true)
|
||||
return
|
||||
}
|
||||
|
||||
// Binary search.
|
||||
//
|
||||
// 二分查找.
|
||||
func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) {
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
@ -343,7 +305,7 @@ func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int)
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = int((min + max) / 2)
|
||||
cmp = a.compareFunc(value, a.array[mid])
|
||||
cmp = a.comparator(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0 : max = mid - 1
|
||||
case cmp > 0 : min = mid + 1
|
||||
@ -354,11 +316,9 @@ func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int)
|
||||
return mid, cmp
|
||||
}
|
||||
|
||||
// Set unique mark to the array,
|
||||
// SetUnique sets unique mark to the array,
|
||||
// which means it does not contain any repeated items.
|
||||
// It also do unique check, remove all repeated items.
|
||||
//
|
||||
// 设置是否允许数组唯一.
|
||||
func (a *SortedIntArray) SetUnique(unique bool) *SortedIntArray {
|
||||
oldUnique := a.unique.Val()
|
||||
a.unique.Set(unique)
|
||||
@ -368,9 +328,7 @@ func (a *SortedIntArray) SetUnique(unique bool) *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Do unique check, remove all repeated items.
|
||||
//
|
||||
// 清理数组中重复的元素项.
|
||||
// Unique uniques the array, clear repeated items.
|
||||
func (a *SortedIntArray) Unique() *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
i := 0
|
||||
@ -378,7 +336,7 @@ func (a *SortedIntArray) Unique() *SortedIntArray {
|
||||
if i == len(a.array) - 1 {
|
||||
break
|
||||
}
|
||||
if a.compareFunc(a.array[i], a.array[i + 1]) == 0 {
|
||||
if a.comparator(a.array[i], a.array[i + 1]) == 0 {
|
||||
a.array = append(a.array[ : i + 1], a.array[i + 1 + 1 : ]...)
|
||||
} else {
|
||||
i++
|
||||
@ -388,9 +346,7 @@ func (a *SortedIntArray) Unique() *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Return a new array, which is a copy of current array.
|
||||
//
|
||||
// 克隆当前数组,返回当前数组的一个拷贝。
|
||||
// Clone returns a new array, which is a copy of current array.
|
||||
func (a *SortedIntArray) Clone() (newArray *SortedIntArray) {
|
||||
a.mu.RLock()
|
||||
array := make([]int, len(a.array))
|
||||
@ -399,9 +355,7 @@ func (a *SortedIntArray) Clone() (newArray *SortedIntArray) {
|
||||
return NewSortedIntArrayFrom(array, !a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear array.
|
||||
//
|
||||
// 清空数据数组。
|
||||
// Clear deletes all items of current array.
|
||||
func (a *SortedIntArray) Clear() *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
@ -411,9 +365,7 @@ func (a *SortedIntArray) Clear() *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
// LockFunc locks writing by callback function <f>.
|
||||
func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -421,9 +373,7 @@ func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
// RLockFunc locks reading by callback function <f>.
|
||||
func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -431,11 +381,10 @@ func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays. The parameter <array> can be any garray type or slice type.
|
||||
// The difference between Merge and Add is Add supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
// Merge merges <array> into current array.
|
||||
// The parameter <array> can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Add(gconv.Ints(v.Slice())...)
|
||||
@ -450,10 +399,9 @@ func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Chunks an array into arrays with size elements.
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// The last chunk may contain less than size elements.
|
||||
//
|
||||
// 将一个数组分割成多个数组,其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
|
||||
func (a *SortedIntArray) Chunk(size int) [][]int {
|
||||
if size < 1 {
|
||||
return nil
|
||||
@ -474,12 +422,9 @@ func (a *SortedIntArray) Chunk(size int) [][]int {
|
||||
return n
|
||||
}
|
||||
|
||||
// Extract a slice of the array(If in concurrent safe usage,
|
||||
// it returns a copy of the slice; else a pointer).
|
||||
// It returns the sequence of elements from the array array as specified
|
||||
// by the offset and length parameters.
|
||||
//
|
||||
// 返回根据offset和size参数所指定的数组中的一段序列。
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
func (a *SortedIntArray) SubSlice(offset, size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -498,18 +443,14 @@ func (a *SortedIntArray) SubSlice(offset, size int) []int {
|
||||
}
|
||||
}
|
||||
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *SortedIntArray) Rand() int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
func (a *SortedIntArray) Rands(size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -526,9 +467,7 @@ func (a *SortedIntArray) Rands(size int) []int {
|
||||
return n
|
||||
}
|
||||
|
||||
// Join array elements with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前数组的元素项,构造成新的字符串返回。
|
||||
// Join joins array elements with a string <glue>.
|
||||
func (a *SortedIntArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -540,4 +479,22 @@ func (a *SortedIntArray) Join(glue string) string {
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// CountValues counts the number of occurrences of all values in the array.
|
||||
func (a *SortedIntArray) CountValues() map[int]int {
|
||||
m := make(map[int]int)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
m[v]++
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// String returns current array as a string.
|
||||
func (a *SortedIntArray) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return fmt.Sprint(a.array)
|
||||
}
|
||||
@ -8,7 +8,8 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
@ -16,64 +17,51 @@ import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// 默认按照从小到大进行排序
|
||||
// It's using increasing order in default.
|
||||
type SortedArray struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
array []interface{} // 底层数组
|
||||
unique *gtype.Bool // 是否要求不能重复
|
||||
compareFunc func(v1, v2 interface{}) int // 比较函数,返回值 -1: v1 < v2;0: v1 == v2;1: v1 > v2
|
||||
mu *rwmutex.RWMutex
|
||||
array []interface{}
|
||||
unique *gtype.Bool // Whether enable unique feature(false)
|
||||
comparator func(v1, v2 interface{}) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
|
||||
}
|
||||
|
||||
// Create an empty sorted array.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
// The param <compareFunc> used to compare values to sort in array,
|
||||
// NewSortedArray creates and returns an empty sorted array.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety, which is false in default.
|
||||
// The param <comparator> used to compare values to sort in array,
|
||||
// if it returns value < 0, means v1 < v2;
|
||||
// if it returns value = 0, means v1 = v2;
|
||||
// if it returns value > 0, means v1 > v2;
|
||||
//
|
||||
// 创建一个空的排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// 参数compareFunc用于指定排序方法:
|
||||
// 如果返回值 < 0, 表示 v1 < v2;
|
||||
// 如果返回值 = 0, 表示 v1 = v2;
|
||||
// 如果返回值 > 0, 表示 v1 > v2;
|
||||
func NewSortedArray(compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
|
||||
return NewSortedArraySize(0, compareFunc, unsafe...)
|
||||
func NewSortedArray(comparator func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
|
||||
return NewSortedArraySize(0, comparator, unsafe...)
|
||||
}
|
||||
|
||||
// Create a sorted array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个指定大小的排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewSortedArraySize(cap int, compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
|
||||
// NewSortedArraySize create and returns an sorted array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedArraySize(cap int, comparator func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
|
||||
return &SortedArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
unique : gtype.NewBool(),
|
||||
array : make([]interface{}, 0, cap),
|
||||
compareFunc : compareFunc,
|
||||
comparator : comparator,
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice变量创建排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewSortedArrayFrom(array []interface{}, compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
|
||||
a := NewSortedArraySize(0, compareFunc, unsafe...)
|
||||
// NewSortedArrayFrom creates and returns an sorted array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedArrayFrom(array []interface{}, comparator func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
|
||||
a := NewSortedArraySize(0, comparator, unsafe...)
|
||||
a.array = array
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.compareFunc(a.array[i], a.array[j]) < 0
|
||||
return a.comparator(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// Create an array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice拷贝创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewSortedArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedArrayFromCopy(array []interface{}, unsafe...bool) *SortedArray {
|
||||
newArray := make([]interface{}, len(array))
|
||||
copy(newArray, array)
|
||||
@ -83,34 +71,30 @@ func NewSortedArrayFromCopy(array []interface{}, unsafe...bool) *SortedArray {
|
||||
}
|
||||
}
|
||||
|
||||
// Set the underlying slice array with the given <array> param.
|
||||
//
|
||||
// 设置底层数组变量.
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
func (a *SortedArray) SetArray(array []interface{}) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.compareFunc(a.array[i], a.array[j]) < 0
|
||||
return a.comparator(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// Sort the array by comparing function.
|
||||
//
|
||||
// 将数组按照比较方法进行排序.
|
||||
// Sort sorts the array in increasing order.
|
||||
// The param <reverse> controls whether sort
|
||||
// in increasing order(default) or decreasing order
|
||||
func (a *SortedArray) Sort() *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.compareFunc(a.array[i], a.array[j]) < 0
|
||||
return a.comparator(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// And values to sorted array, the array always keeps sorted.
|
||||
//
|
||||
// 添加数据项.
|
||||
// Add adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
func (a *SortedArray) Add(values...interface{}) *SortedArray {
|
||||
if len(values) == 0 {
|
||||
return a
|
||||
@ -126,7 +110,6 @@ func (a *SortedArray) Add(values...interface{}) *SortedArray {
|
||||
a.array = append(a.array, value)
|
||||
continue
|
||||
}
|
||||
// 加到指定索引后面
|
||||
if cmp > 0 {
|
||||
index++
|
||||
}
|
||||
@ -137,9 +120,8 @@ func (a *SortedArray) Add(values...interface{}) *SortedArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Get value by index.
|
||||
//
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界。
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *SortedArray) Get(index int) interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -147,13 +129,11 @@ func (a *SortedArray) Get(index int) interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// Remove an item by index.
|
||||
//
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界。
|
||||
// Remove removes an item by index.
|
||||
func (a *SortedArray) Remove(index int) interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// 边界删除判断,以提高删除效率
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
@ -163,15 +143,15 @@ func (a *SortedArray) Remove(index int) interface{} {
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
return value
|
||||
}
|
||||
|
||||
// Push new items to the beginning of array.
|
||||
//
|
||||
// 将数据项添加到数组的最左端(索引为0)。
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
func (a *SortedArray) PopLeft() interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -180,9 +160,7 @@ func (a *SortedArray) PopLeft() interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// Push new items to the end of array.
|
||||
//
|
||||
// 将数据项添加到数组的最右端(索引为length - 1)。
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
func (a *SortedArray) PopRight() interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -192,16 +170,12 @@ func (a *SortedArray) PopRight() interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
func (a *SortedArray) PopRand() interface{} {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
func (a *SortedArray) PopRands(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -217,9 +191,7 @@ func (a *SortedArray) PopRands(size int) []interface{} {
|
||||
return array
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
func (a *SortedArray) PopLefts(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -232,9 +204,7 @@ func (a *SortedArray) PopLefts(size int) []interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop <size> items from the end of array.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
func (a *SortedArray) PopRights(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -247,11 +217,9 @@ func (a *SortedArray) PopRights(size int) []interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// Get items by range, returns array[start:end].
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *SortedArray) Range(start, end int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -277,9 +245,7 @@ func (a *SortedArray) Range(start, end int) []interface{} {
|
||||
return array
|
||||
}
|
||||
|
||||
// Calculate the sum of values in an array.
|
||||
//
|
||||
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
|
||||
// Sum returns the sum of values in an array.
|
||||
func (a *SortedArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -289,9 +255,7 @@ func (a *SortedArray) Sum() (sum int) {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the length of array.
|
||||
//
|
||||
// 数组长度。
|
||||
// Len returns the length of array.
|
||||
func (a *SortedArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
@ -299,11 +263,9 @@ func (a *SortedArray) Len() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// Get the underlying data of array.
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// Slice returns the underlying data of array.
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 返回原始数据数组.
|
||||
func (a *SortedArray) Slice() []interface{} {
|
||||
array := ([]interface{})(nil)
|
||||
if a.mu.IsSafe() {
|
||||
@ -317,25 +279,19 @@ func (a *SortedArray) Slice() []interface{} {
|
||||
return array
|
||||
}
|
||||
|
||||
// Check whether a value exists in the array.
|
||||
//
|
||||
// 查找指定数值是否存在。
|
||||
// Contains checks whether a value exists in the array.
|
||||
func (a *SortedArray) Contains(value interface{}) bool {
|
||||
return a.Search(value) == 0
|
||||
}
|
||||
|
||||
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
|
||||
//
|
||||
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *SortedArray) Search(value interface{}) (index int) {
|
||||
index, _ = a.binSearch(value, true)
|
||||
return
|
||||
}
|
||||
|
||||
// Binary search.
|
||||
//
|
||||
// 二分查找。查找指定数值的索引位置,返回索引位置(具体匹配位置或者最后对比位置)及查找结果
|
||||
// 返回值: 最后比较位置, 比较结果。
|
||||
func (a *SortedArray) binSearch(value interface{}, lock bool)(index int, result int) {
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
@ -350,7 +306,7 @@ func (a *SortedArray) binSearch(value interface{}, lock bool)(index int, result
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = int((min + max) / 2)
|
||||
cmp = a.compareFunc(value, a.array[mid])
|
||||
cmp = a.comparator(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0 : max = mid - 1
|
||||
case cmp > 0 : min = mid + 1
|
||||
@ -361,11 +317,9 @@ func (a *SortedArray) binSearch(value interface{}, lock bool)(index int, result
|
||||
return mid, cmp
|
||||
}
|
||||
|
||||
// Set unique mark to the array,
|
||||
// SetUnique sets unique mark to the array,
|
||||
// which means it does not contain any repeated items.
|
||||
// It also do unique check, remove all repeated items.
|
||||
//
|
||||
// 设置是否允许数组唯一.
|
||||
func (a *SortedArray) SetUnique(unique bool) *SortedArray {
|
||||
oldUnique := a.unique.Val()
|
||||
a.unique.Set(unique)
|
||||
@ -375,9 +329,7 @@ func (a *SortedArray) SetUnique(unique bool) *SortedArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Do unique check, remove all repeated items.
|
||||
//
|
||||
// 清理数组中重复的元素项.
|
||||
// Unique uniques the array, clear repeated items.
|
||||
func (a *SortedArray) Unique() *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -386,7 +338,7 @@ func (a *SortedArray) Unique() *SortedArray {
|
||||
if i == len(a.array) - 1 {
|
||||
break
|
||||
}
|
||||
if a.compareFunc(a.array[i], a.array[i + 1]) == 0 {
|
||||
if a.comparator(a.array[i], a.array[i + 1]) == 0 {
|
||||
a.array = append(a.array[ : i + 1], a.array[i + 1 + 1 : ]...)
|
||||
} else {
|
||||
i++
|
||||
@ -395,20 +347,16 @@ func (a *SortedArray) Unique() *SortedArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Return a new array, which is a copy of current array.
|
||||
//
|
||||
// 克隆当前数组,返回当前数组的一个拷贝。
|
||||
// Clone returns a new array, which is a copy of current array.
|
||||
func (a *SortedArray) Clone() (newArray *SortedArray) {
|
||||
a.mu.RLock()
|
||||
array := make([]interface{}, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewSortedArrayFrom(array, a.compareFunc, !a.mu.IsSafe())
|
||||
return NewSortedArrayFrom(array, a.comparator, !a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear array.
|
||||
//
|
||||
// 清空数据数组。
|
||||
// Clear deletes all items of current array.
|
||||
func (a *SortedArray) Clear() *SortedArray {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
@ -418,9 +366,7 @@ func (a *SortedArray) Clear() *SortedArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
// LockFunc locks writing by callback function <f>.
|
||||
func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -428,9 +374,7 @@ func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
// RLockFunc locks reading by callback function <f>.
|
||||
func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -438,11 +382,10 @@ func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays. The parameter <array> can be any garray type or slice type.
|
||||
// The difference between Merge and Add is Add supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
// Merge merges <array> into current array.
|
||||
// The parameter <array> can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *SortedArray) Merge(array interface{}) *SortedArray {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Add(gconv.Interfaces(v.Slice())...)
|
||||
@ -457,10 +400,9 @@ func (a *SortedArray) Merge(array interface{}) *SortedArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Chunks an array into arrays with size elements.
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// The last chunk may contain less than size elements.
|
||||
//
|
||||
// 将一个数组分割成多个数组,其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
|
||||
func (a *SortedArray) Chunk(size int) [][]interface{} {
|
||||
if size < 1 {
|
||||
return nil
|
||||
@ -481,12 +423,9 @@ func (a *SortedArray) Chunk(size int) [][]interface{} {
|
||||
return n
|
||||
}
|
||||
|
||||
// Extract a slice of the array(If in concurrent safe usage,
|
||||
// it returns a copy of the slice; else a pointer).
|
||||
// It returns the sequence of elements from the array array as specified
|
||||
// by the offset and length parameters.
|
||||
//
|
||||
// 返回根据offset和size参数所指定的数组中的一段序列。
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
func (a *SortedArray) SubSlice(offset, size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -505,18 +444,14 @@ func (a *SortedArray) SubSlice(offset, size int) []interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *SortedArray) Rand() interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
func (a *SortedArray) Rands(size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -533,9 +468,7 @@ func (a *SortedArray) Rands(size int) []interface{} {
|
||||
return n
|
||||
}
|
||||
|
||||
// Join array elements with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前数组的元素项,构造成新的字符串返回。
|
||||
// Join joins array elements with a string <glue>.
|
||||
func (a *SortedArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -547,4 +480,22 @@ func (a *SortedArray) Join(glue string) string {
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// CountValues counts the number of occurrences of all values in the array.
|
||||
func (a *SortedArray) CountValues() map[interface{}]int {
|
||||
m := make(map[interface{}]int)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
m[v]++
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// String returns current array as a string.
|
||||
func (a *SortedArray) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return fmt.Sprint(a.array)
|
||||
}
|
||||
@ -8,7 +8,8 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
@ -17,44 +18,38 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 默认按照从小到大进行排序
|
||||
// It's using increasing order in default.
|
||||
type SortedStringArray struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
array []string // 底层数组
|
||||
unique *gtype.Bool // 是否要求不能重复
|
||||
compareFunc func(v1, v2 string) int // 比较函数,返回值 -1: v1 < v2;0: v1 == v2;1: v1 > v2
|
||||
mu *rwmutex.RWMutex
|
||||
array []string
|
||||
unique *gtype.Bool // Whether enable unique feature(false)
|
||||
comparator func(v1, v2 string) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
|
||||
}
|
||||
|
||||
// Create an empty sorted array.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewSortedStringArray creates and returns an empty sorted array.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedStringArray(unsafe...bool) *SortedStringArray {
|
||||
return NewSortedStringArraySize(0, unsafe...)
|
||||
}
|
||||
|
||||
// Create a sorted array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个指定大小的排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewSortedStringArraySize create and returns an sorted array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedStringArraySize(cap int, unsafe...bool) *SortedStringArray {
|
||||
return &SortedStringArray {
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : make([]string, 0, cap),
|
||||
unique : gtype.NewBool(),
|
||||
compareFunc : func(v1, v2 string) int {
|
||||
comparator : func(v1, v2 string) int {
|
||||
return strings.Compare(v1, v2)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice变量创建排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewSortedStringArrayFrom creates and returns an sorted array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedStringArrayFrom(array []string, unsafe...bool) *SortedStringArray {
|
||||
a := NewSortedStringArraySize(0, unsafe...)
|
||||
a.array = array
|
||||
@ -62,11 +57,9 @@ func NewSortedStringArrayFrom(array []string, unsafe...bool) *SortedStringArray
|
||||
return a
|
||||
}
|
||||
|
||||
// Create an array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice拷贝创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// NewSortedStringArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedStringArrayFromCopy(array []string, unsafe...bool) *SortedStringArray {
|
||||
newArray := make([]string, len(array))
|
||||
copy(newArray, array)
|
||||
@ -76,9 +69,7 @@ func NewSortedStringArrayFromCopy(array []string, unsafe...bool) *SortedStringAr
|
||||
}
|
||||
}
|
||||
|
||||
// Set the underlying slice array with the given <array> param.
|
||||
//
|
||||
// 设置底层数组变量.
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
func (a *SortedStringArray) SetArray(array []string) *SortedStringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -87,9 +78,9 @@ func (a *SortedStringArray) SetArray(array []string) *SortedStringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Sort the array in increasing order.
|
||||
//
|
||||
// 将数组排序(默认从低到高).
|
||||
// Sort sorts the array in increasing order.
|
||||
// The param <reverse> controls whether sort
|
||||
// in increasing order(default) or decreasing order.
|
||||
func (a *SortedStringArray) Sort() *SortedStringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -97,9 +88,7 @@ func (a *SortedStringArray) Sort() *SortedStringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// And values to sorted array, the array always keeps sorted.
|
||||
//
|
||||
// 添加数据项.
|
||||
// Add adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
func (a *SortedStringArray) Add(values...string) *SortedStringArray {
|
||||
if len(values) == 0 {
|
||||
return a
|
||||
@ -115,7 +104,6 @@ func (a *SortedStringArray) Add(values...string) *SortedStringArray {
|
||||
a.array = append(a.array, value)
|
||||
continue
|
||||
}
|
||||
// 加到指定索引后面
|
||||
if cmp > 0 {
|
||||
index++
|
||||
}
|
||||
@ -126,9 +114,8 @@ func (a *SortedStringArray) Add(values...string) *SortedStringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Get value by index.
|
||||
//
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界。
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *SortedStringArray) Get(index int) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -136,13 +123,11 @@ func (a *SortedStringArray) Get(index int) string {
|
||||
return value
|
||||
}
|
||||
|
||||
// Remove an item by index.
|
||||
//
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界。
|
||||
// Remove removes an item by index.
|
||||
func (a *SortedStringArray) Remove(index int) string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// 边界删除判断,以提高删除效率
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
@ -152,15 +137,15 @@ func (a *SortedStringArray) Remove(index int) string {
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
return value
|
||||
}
|
||||
|
||||
// Push new items to the beginning of array.
|
||||
//
|
||||
// 将数据项添加到数组的最左端(索引为0)。
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
func (a *SortedStringArray) PopLeft() string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -169,9 +154,7 @@ func (a *SortedStringArray) PopLeft() string {
|
||||
return value
|
||||
}
|
||||
|
||||
// Push new items to the end of array.
|
||||
//
|
||||
// 将数据项添加到数组的最右端(索引为length - 1)。
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
func (a *SortedStringArray) PopRight() string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -181,16 +164,12 @@ func (a *SortedStringArray) PopRight() string {
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
func (a *SortedStringArray) PopRand() string {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
func (a *SortedStringArray) PopRands(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -206,9 +185,7 @@ func (a *SortedStringArray) PopRands(size int) []string {
|
||||
return array
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
func (a *SortedStringArray) PopLefts(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -221,9 +198,7 @@ func (a *SortedStringArray) PopLefts(size int) []string {
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop <size> items from the end of array.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
func (a *SortedStringArray) PopRights(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -236,11 +211,9 @@ func (a *SortedStringArray) PopRights(size int) []string {
|
||||
return value
|
||||
}
|
||||
|
||||
// Get items by range, returns array[start:end].
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// Range picks and returns items by range, like array[start:end].
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *SortedStringArray) Range(start, end int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -266,9 +239,7 @@ func (a *SortedStringArray) Range(start, end int) []string {
|
||||
return array
|
||||
}
|
||||
|
||||
// Calculate the sum of values in an array.
|
||||
//
|
||||
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
|
||||
// Sum returns the sum of values in an array.
|
||||
func (a *SortedStringArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -278,9 +249,7 @@ func (a *SortedStringArray) Sum() (sum int) {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the length of array.
|
||||
//
|
||||
// 数组长度。
|
||||
// Len returns the length of array.
|
||||
func (a *SortedStringArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
@ -288,11 +257,9 @@ func (a *SortedStringArray) Len() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// Get the underlying data of array.
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// Slice returns the underlying data of array.
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 返回原始数据数组.
|
||||
func (a *SortedStringArray) Slice() []string {
|
||||
array := ([]string)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
@ -306,24 +273,19 @@ func (a *SortedStringArray) Slice() []string {
|
||||
return array
|
||||
}
|
||||
|
||||
// Check whether a value exists in the array.
|
||||
//
|
||||
// 查找指定数值是否存在。
|
||||
// Contains checks whether a value exists in the array.
|
||||
func (a *SortedStringArray) Contains(value string) bool {
|
||||
return a.Search(value) == 0
|
||||
}
|
||||
|
||||
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
|
||||
//
|
||||
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *SortedStringArray) Search(value string) (index int) {
|
||||
index, _ = a.binSearch(value, true)
|
||||
return
|
||||
}
|
||||
|
||||
// Binary search.
|
||||
//
|
||||
// 二分查找.
|
||||
func (a *SortedStringArray) binSearch(value string, lock bool) (index int, result int) {
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
@ -338,7 +300,7 @@ func (a *SortedStringArray) binSearch(value string, lock bool) (index int, resul
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = int((min + max) / 2)
|
||||
cmp = a.compareFunc(value, a.array[mid])
|
||||
cmp = a.comparator(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0 : max = mid - 1
|
||||
case cmp > 0 : min = mid + 1
|
||||
@ -349,11 +311,9 @@ func (a *SortedStringArray) binSearch(value string, lock bool) (index int, resul
|
||||
return mid, cmp
|
||||
}
|
||||
|
||||
// Set unique mark to the array,
|
||||
// SetUnique sets unique mark to the array,
|
||||
// which means it does not contain any repeated items.
|
||||
// It also do unique check, remove all repeated items.
|
||||
//
|
||||
// 设置是否允许数组唯一.
|
||||
func (a *SortedStringArray) SetUnique(unique bool) *SortedStringArray {
|
||||
oldUnique := a.unique.Val()
|
||||
a.unique.Set(unique)
|
||||
@ -363,9 +323,7 @@ func (a *SortedStringArray) SetUnique(unique bool) *SortedStringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Do unique check, remove all repeated items.
|
||||
//
|
||||
// 清理数组中重复的元素项.
|
||||
// Unique uniques the array, clear repeated items.
|
||||
func (a *SortedStringArray) Unique() *SortedStringArray {
|
||||
a.mu.Lock()
|
||||
i := 0
|
||||
@ -373,7 +331,7 @@ func (a *SortedStringArray) Unique() *SortedStringArray {
|
||||
if i == len(a.array) - 1 {
|
||||
break
|
||||
}
|
||||
if a.compareFunc(a.array[i], a.array[i + 1]) == 0 {
|
||||
if a.comparator(a.array[i], a.array[i + 1]) == 0 {
|
||||
a.array = append(a.array[ : i + 1], a.array[i + 1 + 1 : ]...)
|
||||
} else {
|
||||
i++
|
||||
@ -383,9 +341,7 @@ func (a *SortedStringArray) Unique() *SortedStringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Return a new array, which is a copy of current array.
|
||||
//
|
||||
// 克隆当前数组,返回当前数组的一个拷贝。
|
||||
// Clone returns a new array, which is a copy of current array.
|
||||
func (a *SortedStringArray) Clone() (newArray *SortedStringArray) {
|
||||
a.mu.RLock()
|
||||
array := make([]string, len(a.array))
|
||||
@ -394,9 +350,7 @@ func (a *SortedStringArray) Clone() (newArray *SortedStringArray) {
|
||||
return NewSortedStringArrayFrom(array, !a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear array.
|
||||
//
|
||||
// 清空数据数组。
|
||||
// Clear deletes all items of current array.
|
||||
func (a *SortedStringArray) Clear() *SortedStringArray {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
@ -406,9 +360,7 @@ func (a *SortedStringArray) Clear() *SortedStringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
// LockFunc locks writing by callback function <f>.
|
||||
func (a *SortedStringArray) LockFunc(f func(array []string)) *SortedStringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -416,9 +368,7 @@ func (a *SortedStringArray) LockFunc(f func(array []string)) *SortedStringArray
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
// RLockFunc locks reading by callback function <f>.
|
||||
func (a *SortedStringArray) RLockFunc(f func(array []string)) *SortedStringArray {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -426,11 +376,10 @@ func (a *SortedStringArray) RLockFunc(f func(array []string)) *SortedStringArray
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays. The parameter <array> can be any garray type or slice type.
|
||||
// The difference between Merge and Add is Add supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
// Merge merges <array> into current array.
|
||||
// The parameter <array> can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *SortedStringArray) Merge(array interface{}) *SortedStringArray {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Add(gconv.Strings(v.Slice())...)
|
||||
@ -445,10 +394,9 @@ func (a *SortedStringArray) Merge(array interface{}) *SortedStringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Chunks an array into arrays with size elements.
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// The last chunk may contain less than size elements.
|
||||
//
|
||||
// 将一个数组分割成多个数组,其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
|
||||
func (a *SortedStringArray) Chunk(size int) [][]string {
|
||||
if size < 1 {
|
||||
return nil
|
||||
@ -469,12 +417,9 @@ func (a *SortedStringArray) Chunk(size int) [][]string {
|
||||
return n
|
||||
}
|
||||
|
||||
// Extract a slice of the array(If in concurrent safe usage,
|
||||
// it returns a copy of the slice; else a pointer).
|
||||
// It returns the sequence of elements from the array array as specified
|
||||
// by the offset and length parameters.
|
||||
//
|
||||
// 返回根据offset和size参数所指定的数组中的一段序列。
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
func (a *SortedStringArray) SubSlice(offset, size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -493,18 +438,14 @@ func (a *SortedStringArray) SubSlice(offset, size int) []string {
|
||||
}
|
||||
}
|
||||
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *SortedStringArray) Rand() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
func (a *SortedStringArray) Rands(size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -521,9 +462,7 @@ func (a *SortedStringArray) Rands(size int) []string {
|
||||
return n
|
||||
}
|
||||
|
||||
// Join array elements with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前数组的元素项,构造成新的字符串返回。
|
||||
// Join joins array elements with a string <glue>.
|
||||
func (a *SortedStringArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -535,4 +474,22 @@ func (a *SortedStringArray) Join(glue string) string {
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// CountValues counts the number of occurrences of all values in the array.
|
||||
func (a *SortedStringArray) CountValues() map[string]int {
|
||||
m := make(map[string]int)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
m[v]++
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// String returns current array as a string.
|
||||
func (a *SortedStringArray) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return fmt.Sprint(a.array)
|
||||
}
|
||||
@ -22,7 +22,7 @@ type (
|
||||
Element = list.Element
|
||||
)
|
||||
|
||||
// 获得一个变长链表指针
|
||||
// New creates and returns a new empty doubly linked list.
|
||||
func New(unsafe...bool) *List {
|
||||
return &List {
|
||||
mu : rwmutex.New(unsafe...),
|
||||
@ -30,7 +30,7 @@ func New(unsafe...bool) *List {
|
||||
}
|
||||
}
|
||||
|
||||
// 往链表头入栈数据项
|
||||
// PushFront inserts a new element <e> with value <v> at the front of list <l> and returns <e>.
|
||||
func (l *List) PushFront(v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
e = l.list.PushFront(v)
|
||||
@ -38,7 +38,7 @@ func (l *List) PushFront(v interface{}) (e *Element) {
|
||||
return
|
||||
}
|
||||
|
||||
// 往链表尾入栈数据项
|
||||
// PushBack inserts a new element <e> with value <v> at the back of list <l> and returns <e>.
|
||||
func (l *List) PushBack(v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
e = l.list.PushBack(v)
|
||||
@ -46,8 +46,8 @@ func (l *List) PushBack(v interface{}) (e *Element) {
|
||||
return
|
||||
}
|
||||
|
||||
// 批量往链表头入栈数据项
|
||||
func (l *List) BatchPushFront(values []interface{}) {
|
||||
// PushFronts inserts multiple new elements with values <values> at the front of list <l>.
|
||||
func (l *List) PushFronts(values []interface{}) {
|
||||
l.mu.Lock()
|
||||
for _, v := range values {
|
||||
l.list.PushFront(v)
|
||||
@ -55,8 +55,8 @@ func (l *List) BatchPushFront(values []interface{}) {
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量往链表尾入栈数据项
|
||||
func (l *List) BatchPushBack(values []interface{}) {
|
||||
// PushBacks inserts multiple new elements with values <values> at the back of list <l>.
|
||||
func (l *List) PushBacks(values []interface{}) {
|
||||
l.mu.Lock()
|
||||
for _, v := range values {
|
||||
l.list.PushBack(v)
|
||||
@ -64,7 +64,7 @@ func (l *List) BatchPushBack(values []interface{}) {
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// 从链表尾端出栈数据项(删除)
|
||||
// PopBack removes the element from back of <l> and returns the value of the element.
|
||||
func (l *List) PopBack() (value interface{}) {
|
||||
l.mu.Lock()
|
||||
if e := l.list.Back(); e != nil {
|
||||
@ -74,7 +74,7 @@ func (l *List) PopBack() (value interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// 从链表头端出栈数据项(删除)
|
||||
// PopFront removes the element from front of <l> and returns the value of the element.
|
||||
func (l *List) PopFront() (value interface{}) {
|
||||
l.mu.Lock()
|
||||
if e := l.list.Front(); e != nil {
|
||||
@ -84,8 +84,9 @@ func (l *List) PopFront() (value interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// 批量从链表尾端出栈数据项(删除)
|
||||
func (l *List) BatchPopBack(max int) (values []interface{}) {
|
||||
// PopBacks removes <max> elements from back of <l>
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *List) PopBacks(max int) (values []interface{}) {
|
||||
l.mu.Lock()
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
@ -103,8 +104,9 @@ func (l *List) BatchPopBack(max int) (values []interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// 批量从链表头端出栈数据项(删除)
|
||||
func (l *List) BatchPopFront(max int) (values []interface{}) {
|
||||
// PopFronts removes <max> elements from front of <l>
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *List) PopFronts(max int) (values []interface{}) {
|
||||
l.mu.RLock()
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
@ -122,17 +124,19 @@ func (l *List) BatchPopFront(max int) (values []interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// 批量从链表尾端依次获取所有数据(删除)
|
||||
// PopBackAll removes all elements from back of <l>
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *List) PopBackAll() []interface{} {
|
||||
return l.BatchPopBack(-1)
|
||||
return l.PopBacks(-1)
|
||||
}
|
||||
|
||||
// 批量从链表头端依次获取所有数据(删除)
|
||||
// PopFrontAll removes all elements from front of <l>
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *List) PopFrontAll() []interface{} {
|
||||
return l.BatchPopFront(-1)
|
||||
return l.PopFronts(-1)
|
||||
}
|
||||
|
||||
// 从链表头获取所有数据(不删除)
|
||||
// FrontAll copies and returns values of all elements from front of <l> as slice.
|
||||
func (l *List) FrontAll() (values []interface{}) {
|
||||
l.mu.RLock()
|
||||
length := l.list.Len()
|
||||
@ -146,7 +150,7 @@ func (l *List) FrontAll() (values []interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// 从链表尾获取所有数据(不删除)
|
||||
// BackAll copies and returns values of all elements from back of <l> as slice.
|
||||
func (l *List) BackAll() (values []interface{}) {
|
||||
l.mu.RLock()
|
||||
length := l.list.Len()
|
||||
@ -160,8 +164,8 @@ func (l *List) BackAll() (values []interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取链表头值(不删除)
|
||||
func (l *List) FrontItem() (value interface{}) {
|
||||
// FrontValue returns value of the first element of <l> or nil if the list is empty.
|
||||
func (l *List) FrontValue() (value interface{}) {
|
||||
l.mu.RLock()
|
||||
if e := l.list.Front(); e != nil {
|
||||
value = e.Value
|
||||
@ -170,8 +174,8 @@ func (l *List) FrontItem() (value interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取链表尾值(不删除)
|
||||
func (l *List) BackItem() (value interface{}) {
|
||||
// BackValue returns value of the last element of <l> or nil if the list is empty.
|
||||
func (l *List) BackValue() (value interface{}) {
|
||||
l.mu.RLock()
|
||||
if e := l.list.Back(); e != nil {
|
||||
value = e.Value
|
||||
@ -180,7 +184,7 @@ func (l *List) BackItem() (value interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取表头指针
|
||||
// Front returns the first element of list <l> or nil if the list is empty.
|
||||
func (l *List) Front() (e *Element) {
|
||||
l.mu.RLock()
|
||||
e = l.list.Front()
|
||||
@ -188,7 +192,7 @@ func (l *List) Front() (e *Element) {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取表位指针
|
||||
// Back returns the last element of list <l> or nil if the list is empty.
|
||||
func (l *List) Back() (e *Element) {
|
||||
l.mu.RLock()
|
||||
e = l.list.Back()
|
||||
@ -196,7 +200,8 @@ func (l *List) Back() (e *Element) {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取链表长度
|
||||
// Len returns the number of elements of list <l>.
|
||||
// The complexity is O(1).
|
||||
func (l *List) Len() (length int) {
|
||||
l.mu.RLock()
|
||||
length = l.list.Len()
|
||||
@ -204,30 +209,44 @@ func (l *List) Len() (length int) {
|
||||
return
|
||||
}
|
||||
|
||||
// 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 *List) MoveBefore(e, p *Element) {
|
||||
l.mu.Lock()
|
||||
l.list.MoveBefore(e, p)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// 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 *List) MoveAfter(e, p *Element) {
|
||||
l.mu.Lock()
|
||||
l.list.MoveAfter(e, p)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// 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 *List) MoveToFront(e *Element) {
|
||||
l.mu.Lock()
|
||||
l.list.MoveToFront(e)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// 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 *List) MoveToBack(e *Element) {
|
||||
l.mu.Lock()
|
||||
l.list.MoveToBack(e)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// 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 *List) PushBackList(other *List) {
|
||||
if l != other {
|
||||
other.mu.RLock()
|
||||
@ -238,6 +257,8 @@ func (l *List) PushBackList(other *List) {
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// 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 *List) PushFrontList(other *List) {
|
||||
if l != other {
|
||||
other.mu.RLock()
|
||||
@ -248,7 +269,9 @@ func (l *List) PushFrontList(other *List) {
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// 在list中元素项p之后插入一个值为v的元素,并返回该元素,如果mark不是list中元素,则list不改变。
|
||||
// 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 *List) InsertAfter(v interface{}, p *Element) (e *Element) {
|
||||
l.mu.Lock()
|
||||
e = l.list.InsertAfter(v, p)
|
||||
@ -256,7 +279,9 @@ func (l *List) InsertAfter(v interface{}, p *Element) (e *Element) {
|
||||
return
|
||||
}
|
||||
|
||||
// 在list中元素项p之前插入一个值为v的元素,并返回该元素,如果mark不是list中元素,则list不改变。
|
||||
// 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 *List) InsertBefore(v interface{}, p *Element) (e *Element) {
|
||||
l.mu.Lock()
|
||||
e = l.list.InsertBefore(v, p)
|
||||
@ -264,7 +289,9 @@ func (l *List) InsertBefore(v interface{}, p *Element) (e *Element) {
|
||||
return
|
||||
}
|
||||
|
||||
// 删除数据项e, 并返回删除项的元素项
|
||||
// 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 *List) Remove(e *Element) (value interface{}) {
|
||||
l.mu.Lock()
|
||||
value = l.list.Remove(e)
|
||||
@ -272,8 +299,8 @@ func (l *List) Remove(e *Element) (value interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// 批量删除数据项
|
||||
func (l *List) BatchRemove(es []*Element) {
|
||||
// Removes removes multiple elements <es> from <l> if <es> are elements of list <l>.
|
||||
func (l *List) Removes(es []*Element) {
|
||||
l.mu.Lock()
|
||||
for _, e := range es {
|
||||
l.list.Remove(e)
|
||||
@ -282,23 +309,63 @@ func (l *List) BatchRemove(es []*Element) {
|
||||
return
|
||||
}
|
||||
|
||||
// 删除所有数据项
|
||||
// RemoveAll removes all elements from list <l>.
|
||||
func (l *List) RemoveAll() {
|
||||
l.mu.Lock()
|
||||
l.list = list.New()
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// 读锁操作
|
||||
// See RemoveAll().
|
||||
func (l *List) Clear() {
|
||||
l.RemoveAll()
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
|
||||
func (l *List) RLockFunc(f func(list *list.List)) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
f(l.list)
|
||||
}
|
||||
|
||||
// 写锁操作
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (l *List) LockFunc(f func(list *list.List)) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
f(l.list)
|
||||
}
|
||||
|
||||
// Iterator is alias of IteratorAsc.
|
||||
func (l *List) Iterator(f func (e *Element) bool) {
|
||||
l.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the list in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (l *List) IteratorAsc(f func (e *Element) bool) {
|
||||
l.mu.RLock()
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, l.list.Front(); i < length; i, e = i + 1, e.Next() {
|
||||
if !f(e) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the list in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (l *List) IteratorDesc(f func (e *Element) bool) {
|
||||
l.mu.RLock()
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, l.list.Back(); i < length; i, e = i + 1, e.Prev() {
|
||||
if !f(e) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
@ -4,315 +4,41 @@
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gmap provides concurrent-safe/unsafe maps.
|
||||
// Package gmap provides concurrent-safe/unsafe map containers.
|
||||
package gmap
|
||||
|
||||
import "github.com/gogf/gf/g/internal/rwmutex"
|
||||
|
||||
type Map struct {
|
||||
mu *rwmutex.RWMutex
|
||||
m map[interface{}]interface{}
|
||||
}
|
||||
// Map based on hash table, alias of AnyAnyMap.
|
||||
type Map = AnyAnyMap
|
||||
type HashMap = AnyAnyMap
|
||||
|
||||
// New returns an empty hash map.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func New(unsafe ...bool) *Map {
|
||||
return NewMap(unsafe...)
|
||||
return NewAnyAnyMap(unsafe...)
|
||||
}
|
||||
|
||||
// Alias of New. See New.
|
||||
func NewMap(unsafe ...bool) *Map {
|
||||
return &Map{
|
||||
m : make(map[interface{}]interface{}),
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewFrom returns a hash map from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// NewFrom returns a hash map from given map <data>.
|
||||
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewFrom(m map[interface{}]interface{}, unsafe...bool) *Map {
|
||||
return &Map{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewFrom(data map[interface{}]interface{}, unsafe...bool) *Map {
|
||||
return NewAnyAnyMapFrom(data, unsafe...)
|
||||
}
|
||||
|
||||
// NewFromArray returns a hash map from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewFromArray(keys []interface{}, values []interface{}, unsafe...bool) *Map {
|
||||
m := make(map[interface{}]interface{})
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = interface{}(nil)
|
||||
}
|
||||
}
|
||||
return &Map{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
// NewHashMap returns an empty hash map.
|
||||
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewHashMap(unsafe ...bool) *Map {
|
||||
return NewAnyAnyMap(unsafe...)
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *Map) Iterator(f func (k interface{}, v interface{}) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *Map) Clone(unsafe ...bool) *Map {
|
||||
return NewFrom(gm.Map(), unsafe ...)
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *Map) Map() map[interface{}]interface{} {
|
||||
m := make(map[interface{}]interface{})
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *Map) Set(key interface{}, val interface{}) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *Map) BatchSet(m map[interface{}]interface{}) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *Map) Get(key interface{}) interface{} {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the hash map,
|
||||
// and its return value will be set to the map with <key>.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *Map) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface {}); ok {
|
||||
value = f()
|
||||
}
|
||||
gm.m[key] = value
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *Map) GetOrSet(key interface{}, value interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (gm *Map) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *Map) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *Map) SetIfNotExist(key interface{}, value interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *Map) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *Map) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *Map) BatchRemove(keys []interface{}) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *Map) Remove(key interface{}) interface{} {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *Map) Keys() []interface{} {
|
||||
gm.mu.RLock()
|
||||
keys := make([]interface{}, 0)
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (gm *Map) Values() []interface{} {
|
||||
gm.mu.RLock()
|
||||
vals := make([]interface{}, 0)
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *Map) Contains(key interface{}) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (gm *Map) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *Map) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *Map) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[interface{}]interface{})
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *Map) LockFunc(f func(m map[interface{}]interface{})) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *Map) RLockFunc(f func(m map[interface{}]interface{})) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
func (gm *Map) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[interface{}]interface{}, len(gm.m))
|
||||
for i, v := range gm.m {
|
||||
n[v] = i
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *Map) Merge(other *Map) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
// NewHashMapFrom returns a hash map from given map <data>.
|
||||
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewHashMapFrom(data map[interface{}]interface{}, unsafe...bool) *Map {
|
||||
return NewAnyAnyMapFrom(data, unsafe...)
|
||||
}
|
||||
330
g/container/gmap/gmap_hash_any_any_map.go
Normal file
330
g/container/gmap/gmap_hash_any_any_map.go
Normal file
@ -0,0 +1,330 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type AnyAnyMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
data map[interface{}]interface{}
|
||||
}
|
||||
|
||||
// NewAnyAnyMap returns an empty hash map.
|
||||
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewAnyAnyMap(unsafe ...bool) *AnyAnyMap {
|
||||
return &AnyAnyMap{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
data : make(map[interface{}]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// NewAnyAnyMapFrom returns a hash map from given map <data>.
|
||||
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewAnyAnyMapFrom(data map[interface{}]interface{}, unsafe...bool) *AnyAnyMap {
|
||||
return &AnyAnyMap{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
data : data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *AnyAnyMap) Iterator(f func (k interface{}, v interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.data {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *AnyAnyMap) Clone(unsafe ...bool) *AnyAnyMap {
|
||||
return NewFrom(m.Map(), unsafe ...)
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (m *AnyAnyMap) Map() map[interface{}]interface{} {
|
||||
m.mu.RLock()
|
||||
data := make(map[interface{}]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *AnyAnyMap) Set(key interface{}, val interface{}) {
|
||||
m.mu.Lock()
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *AnyAnyMap) Sets(data map[interface{}]interface{}) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given <key>.
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *AnyAnyMap) Search(key interface{}) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *AnyAnyMap) Get(key interface{}) interface{} {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the hash map,
|
||||
// and its return value will be set to the map with <key>.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (m *AnyAnyMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface {}); ok {
|
||||
value = f()
|
||||
}
|
||||
m.data[key] = value
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (m *AnyAnyMap) GetOrSet(key interface{}, value interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (m *AnyAnyMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (m *AnyAnyMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with the value by given <key>.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVar(key interface{}) *gvar.Var {
|
||||
return gvar.New(m.Get(key), true)
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value), true)
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f), true)
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f), true)
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *AnyAnyMap) SetIfNotExist(key interface{}, value interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *AnyAnyMap) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (m *AnyAnyMap) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *AnyAnyMap) Remove(key interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *AnyAnyMap) Removes(keys []interface{}) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *AnyAnyMap) Keys() []interface{} {
|
||||
m.mu.RLock()
|
||||
keys := make([]interface{}, len(m.data))
|
||||
index := 0
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *AnyAnyMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
values := make([]interface{}, len(m.data))
|
||||
index := 0
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *AnyAnyMap) Contains(key interface{}) bool {
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *AnyAnyMap) Size() int {
|
||||
m.mu.RLock()
|
||||
length := len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *AnyAnyMap) IsEmpty() bool {
|
||||
m.mu.RLock()
|
||||
empty := len(m.data) == 0
|
||||
m.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *AnyAnyMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[interface{}]interface{})
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (m *AnyAnyMap) LockFunc(f func(m map[interface{}]interface{})) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
|
||||
func (m *AnyAnyMap) RLockFunc(f func(m map[interface{}]interface{})) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *AnyAnyMap) Flip() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
n := make(map[interface{}]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
n[v] = k
|
||||
}
|
||||
m.data = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <m>.
|
||||
func (m *AnyAnyMap) Merge(other *AnyAnyMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
334
g/container/gmap/gmap_hash_int_any_map.go
Normal file
334
g/container/gmap/gmap_hash_int_any_map.go
Normal file
@ -0,0 +1,334 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type IntAnyMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
data map[int]interface{}
|
||||
}
|
||||
|
||||
// NewIntAnyMap returns an empty IntAnyMap object.
|
||||
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewIntAnyMap(unsafe...bool) *IntAnyMap {
|
||||
return &IntAnyMap{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
data : make(map[int]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntAnyMapFrom returns a hash map from given map <data>.
|
||||
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntAnyMapFrom(data map[int]interface{}, unsafe...bool) *IntAnyMap {
|
||||
return &IntAnyMap{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
data : data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *IntAnyMap) Iterator(f func (k int, v interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.data {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *IntAnyMap) Clone() *IntAnyMap {
|
||||
return NewIntAnyMapFrom(m.Map(), !m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (m *IntAnyMap) Map() map[int]interface{} {
|
||||
m.mu.RLock()
|
||||
data := make(map[int]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *IntAnyMap) Set(key int, val interface{}) {
|
||||
m.mu.Lock()
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *IntAnyMap) Sets(data map[int]interface{}) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given <key>.
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *IntAnyMap) Search(key int) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *IntAnyMap) Get(key int) (interface{}) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the hash map,
|
||||
// and its return value will be set to the map with <key>.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (m *IntAnyMap) doSetWithLockCheck(key int, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface {}); ok {
|
||||
value = f()
|
||||
}
|
||||
if value != nil {
|
||||
m.data[key] = value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (m *IntAnyMap) GetOrSet(key int, value interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
func (m *IntAnyMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (m *IntAnyMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with the value by given <key>.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVar(key int) *gvar.Var {
|
||||
return gvar.New(m.Get(key), true)
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVarOrSet(key int, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value), true)
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVarOrSetFunc(key int, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f), true)
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVarOrSetFuncLock(key int, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f), true)
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *IntAnyMap) SetIfNotExist(key int, value interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *IntAnyMap) SetIfNotExistFunc(key int, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (m *IntAnyMap) SetIfNotExistFuncLock(key int, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *IntAnyMap) Removes(keys []int) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *IntAnyMap) Remove(key int) interface{} {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *IntAnyMap) Keys() []int {
|
||||
m.mu.RLock()
|
||||
keys := make([]int, len(m.data))
|
||||
index := 0
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *IntAnyMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
values := make([]interface{}, len(m.data))
|
||||
index := 0
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *IntAnyMap) Contains(key int) bool {
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *IntAnyMap) Size() int {
|
||||
m.mu.RLock()
|
||||
length := len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *IntAnyMap) IsEmpty() bool {
|
||||
m.mu.RLock()
|
||||
empty := len(m.data) == 0
|
||||
m.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *IntAnyMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[int]interface{})
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (m *IntAnyMap) LockFunc(f func(m map[int]interface{})) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
|
||||
func (m *IntAnyMap) RLockFunc(f func(m map[int]interface{})) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *IntAnyMap) Flip() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
n := make(map[int]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
n[gconv.Int(v)] = k
|
||||
}
|
||||
m.data = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <m>.
|
||||
func (m *IntAnyMap) Merge(other *IntAnyMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
308
g/container/gmap/gmap_hash_int_int_map.go
Normal file
308
g/container/gmap/gmap_hash_int_int_map.go
Normal file
@ -0,0 +1,308 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type IntIntMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
data map[int]int
|
||||
}
|
||||
|
||||
// NewIntIntMap returns an empty IntIntMap object.
|
||||
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewIntIntMap(unsafe...bool) *IntIntMap {
|
||||
return &IntIntMap{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
data : make(map[int]int),
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntIntMapFrom returns a hash map from given map <data>.
|
||||
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntIntMapFrom(data map[int]int, unsafe...bool) *IntIntMap {
|
||||
return &IntIntMap{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
data : data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *IntIntMap) Iterator(f func (k int, v int) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.data {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *IntIntMap) Clone() *IntIntMap {
|
||||
return NewIntIntMapFrom(m.Map(), !m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (m *IntIntMap) Map() map[int]int {
|
||||
m.mu.RLock()
|
||||
data := make(map[int]int, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *IntIntMap) Set(key int, val int) {
|
||||
m.mu.Lock()
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *IntIntMap) Sets(data map[int]int) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given <key>.
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *IntIntMap) Search(key int) (value int, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *IntIntMap) Get(key int) (int) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (m *IntIntMap) doSetWithLockCheck(key int, value int) int {
|
||||
m.mu.Lock()
|
||||
if v, ok := m.data[key]; ok {
|
||||
m.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
m.data[key] = value
|
||||
m.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (m *IntIntMap) GetOrSet(key int, value int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
func (m *IntIntMap) GetOrSetFunc(key int, f func() int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (m *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
v = f()
|
||||
m.data[key] = v
|
||||
return v
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *IntIntMap) SetIfNotExist(key int, value int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *IntIntMap) SetIfNotExistFunc(key int, f func() int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (m *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *IntIntMap) Removes(keys []int) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *IntIntMap) Remove(key int) int {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *IntIntMap) Keys() []int {
|
||||
m.mu.RLock()
|
||||
keys := make([]int, len(m.data))
|
||||
index := 0
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *IntIntMap) Values() []int {
|
||||
m.mu.RLock()
|
||||
values := make([]int, len(m.data))
|
||||
index := 0
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *IntIntMap) Contains(key int) bool {
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *IntIntMap) Size() int {
|
||||
m.mu.RLock()
|
||||
length := len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *IntIntMap) IsEmpty() bool {
|
||||
m.mu.RLock()
|
||||
empty := len(m.data) == 0
|
||||
m.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *IntIntMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[int]int)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (m *IntIntMap) LockFunc(f func(m map[int]int)) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
|
||||
func (m *IntIntMap) RLockFunc(f func(m map[int]int)) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *IntIntMap) Flip() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
n := make(map[int]int, len(m.data))
|
||||
for k, v := range m.data {
|
||||
n[v] = k
|
||||
}
|
||||
m.data = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <m>.
|
||||
func (m *IntIntMap) Merge(other *IntIntMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
309
g/container/gmap/gmap_hash_int_str_map.go
Normal file
309
g/container/gmap/gmap_hash_int_str_map.go
Normal file
@ -0,0 +1,309 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type IntStrMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
data map[int]string
|
||||
}
|
||||
|
||||
// NewIntStrMap returns an empty IntStrMap object.
|
||||
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewIntStrMap(unsafe ...bool) *IntStrMap {
|
||||
return &IntStrMap{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
data : make(map[int]string),
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntStrMapFrom returns a hash map from given map <data>.
|
||||
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntStrMapFrom(data map[int]string, unsafe ...bool) *IntStrMap {
|
||||
return &IntStrMap{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
data : data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *IntStrMap) Iterator(f func(k int, v string) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.data {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *IntStrMap) Clone() *IntStrMap {
|
||||
return NewIntStrMapFrom(m.Map(), !m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (m *IntStrMap) Map() map[int]string {
|
||||
m.mu.RLock()
|
||||
data := make(map[int]string, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *IntStrMap) Set(key int, val string) {
|
||||
m.mu.Lock()
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *IntStrMap) Sets(data map[int]string) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given <key>.
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *IntStrMap) Search(key int) (value string, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *IntStrMap) Get(key int) string {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (m *IntStrMap) doSetWithLockCheck(key int, value string) string {
|
||||
m.mu.Lock()
|
||||
if v, ok := m.data[key]; ok {
|
||||
m.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
m.data[key] = value
|
||||
m.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (m *IntStrMap) GetOrSet(key int, value string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
func (m *IntStrMap) GetOrSetFunc(key int, f func() string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (m *IntStrMap) GetOrSetFuncLock(key int, f func() string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
v = f()
|
||||
m.data[key] = v
|
||||
return v
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *IntStrMap) SetIfNotExist(key int, value string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *IntStrMap) SetIfNotExistFunc(key int, f func() string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (m *IntStrMap) SetIfNotExistFuncLock(key int, f func() string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *IntStrMap) Removes(keys []int) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *IntStrMap) Remove(key int) string {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *IntStrMap) Keys() []int {
|
||||
m.mu.RLock()
|
||||
keys := make([]int, len(m.data))
|
||||
index := 0
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *IntStrMap) Values() []string {
|
||||
m.mu.RLock()
|
||||
values := make([]string, len(m.data))
|
||||
index := 0
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *IntStrMap) Contains(key int) bool {
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *IntStrMap) Size() int {
|
||||
m.mu.RLock()
|
||||
length := len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *IntStrMap) IsEmpty() bool {
|
||||
m.mu.RLock()
|
||||
empty := len(m.data) == 0
|
||||
m.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *IntStrMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[int]string)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (m *IntStrMap) LockFunc(f func(m map[int]string)) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
|
||||
func (m *IntStrMap) RLockFunc(f func(m map[int]string)) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *IntStrMap) Flip() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
n := make(map[int]string, len(m.data))
|
||||
for k, v := range m.data {
|
||||
n[gconv.Int(v)] = gconv.String(k)
|
||||
}
|
||||
m.data = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <m>.
|
||||
func (m *IntStrMap) Merge(other *IntStrMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
334
g/container/gmap/gmap_hash_str_any_map.go
Normal file
334
g/container/gmap/gmap_hash_str_any_map.go
Normal file
@ -0,0 +1,334 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type StrAnyMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
data map[string]interface{}
|
||||
}
|
||||
|
||||
// NewStrAnyMap returns an empty StrAnyMap object.
|
||||
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewStrAnyMap(unsafe ...bool) *StrAnyMap {
|
||||
return &StrAnyMap{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
data : make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStrAnyMapFrom returns a hash map from given map <data>.
|
||||
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStrAnyMapFrom(data map[string]interface{}, unsafe ...bool) *StrAnyMap {
|
||||
return &StrAnyMap{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
data : data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *StrAnyMap) Iterator(f func(k string, v interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.data {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *StrAnyMap) Clone() *StrAnyMap {
|
||||
return NewStrAnyMapFrom(m.Map(), !m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (m *StrAnyMap) Map() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *StrAnyMap) Set(key string, val interface{}) {
|
||||
m.mu.Lock()
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *StrAnyMap) Sets(data map[string]interface{}) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given <key>.
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *StrAnyMap) Search(key string) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *StrAnyMap) Get(key string) interface{} {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the hash map,
|
||||
// and its return value will be set to the map with <key>.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (m *StrAnyMap) doSetWithLockCheck(key string, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface{}); ok {
|
||||
value = f()
|
||||
}
|
||||
if value != nil {
|
||||
m.data[key] = value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (m *StrAnyMap) GetOrSet(key string, value interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (m *StrAnyMap) GetOrSetFunc(key string, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (m *StrAnyMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with the value by given <key>.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVar(key string) *gvar.Var {
|
||||
return gvar.New(m.Get(key), true)
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVarOrSet(key string, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value), true)
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVarOrSetFunc(key string, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f), true)
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVarOrSetFuncLock(key string, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f), true)
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *StrAnyMap) SetIfNotExist(key string, value interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *StrAnyMap) SetIfNotExistFunc(key string, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (m *StrAnyMap) SetIfNotExistFuncLock(key string, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *StrAnyMap) Removes(keys []string) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *StrAnyMap) Remove(key string) interface{} {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *StrAnyMap) Keys() []string {
|
||||
m.mu.RLock()
|
||||
keys := make([]string, len(m.data))
|
||||
index := 0
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *StrAnyMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
values := make([]interface{}, len(m.data))
|
||||
index := 0
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *StrAnyMap) Contains(key string) bool {
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *StrAnyMap) Size() int {
|
||||
m.mu.RLock()
|
||||
length := len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *StrAnyMap) IsEmpty() bool {
|
||||
m.mu.RLock()
|
||||
empty := len(m.data) == 0
|
||||
m.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *StrAnyMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[string]interface{})
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (m *StrAnyMap) LockFunc(f func(m map[string]interface{})) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
|
||||
func (m *StrAnyMap) RLockFunc(f func(m map[string]interface{})) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *StrAnyMap) Flip() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
n := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
n[gconv.String(v)] = k
|
||||
}
|
||||
m.data = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <m>.
|
||||
func (m *StrAnyMap) Merge(other *StrAnyMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
312
g/container/gmap/gmap_hash_str_int_map.go
Normal file
312
g/container/gmap/gmap_hash_str_int_map.go
Normal file
@ -0,0 +1,312 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type StrIntMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
data map[string]int
|
||||
}
|
||||
|
||||
// NewStrIntMap returns an empty StrIntMap object.
|
||||
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewStrIntMap(unsafe ...bool) *StrIntMap {
|
||||
return &StrIntMap{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
data : make(map[string]int),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStrIntMapFrom returns a hash map from given map <data>.
|
||||
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStrIntMapFrom(data map[string]int, unsafe ...bool) *StrIntMap {
|
||||
return &StrIntMap{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
data : data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *StrIntMap) Iterator(f func(k string, v int) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.data {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *StrIntMap) Clone() *StrIntMap {
|
||||
return NewStrIntMapFrom(m.Map(), !m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (m *StrIntMap) Map() map[string]int {
|
||||
m.mu.RLock()
|
||||
data := make(map[string]int, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *StrIntMap) Set(key string, val int) {
|
||||
m.mu.Lock()
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *StrIntMap) Sets(data map[string]int) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given <key>.
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *StrIntMap) Search(key string) (value int, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *StrIntMap) Get(key string) int {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (m *StrIntMap) doSetWithLockCheck(key string, value int) int {
|
||||
m.mu.Lock()
|
||||
if v, ok := m.data[key]; ok {
|
||||
m.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
m.data[key] = value
|
||||
m.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (m *StrIntMap) GetOrSet(key string, value int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (m *StrIntMap) GetOrSetFunc(key string, f func() int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (m *StrIntMap) GetOrSetFuncLock(key string, f func() int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
v = f()
|
||||
m.data[key] = v
|
||||
return v
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *StrIntMap) SetIfNotExist(key string, value int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *StrIntMap) SetIfNotExistFunc(key string, f func() int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (m *StrIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *StrIntMap) Removes(keys []string) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *StrIntMap) Remove(key string) int {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *StrIntMap) Keys() []string {
|
||||
m.mu.RLock()
|
||||
keys := make([]string, len(m.data))
|
||||
index := 0
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *StrIntMap) Values() []int {
|
||||
m.mu.RLock()
|
||||
values := make([]int, len(m.data))
|
||||
index := 0
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *StrIntMap) Contains(key string) bool {
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *StrIntMap) Size() int {
|
||||
m.mu.RLock()
|
||||
length := len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *StrIntMap) IsEmpty() bool {
|
||||
m.mu.RLock()
|
||||
empty := len(m.data) == 0
|
||||
m.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *StrIntMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[string]int)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (m *StrIntMap) LockFunc(f func(m map[string]int)) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
|
||||
func (m *StrIntMap) RLockFunc(f func(m map[string]int)) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *StrIntMap) Flip() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
n := make(map[string]int, len(m.data))
|
||||
for k, v := range m.data {
|
||||
n[gconv.String(v)] = gconv.Int(k)
|
||||
}
|
||||
m.data = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <m>.
|
||||
func (m *StrIntMap) Merge(other *StrIntMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
311
g/container/gmap/gmap_hash_str_str_map.go
Normal file
311
g/container/gmap/gmap_hash_str_str_map.go
Normal file
@ -0,0 +1,311 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type StrStrMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
data map[string]string
|
||||
}
|
||||
|
||||
// NewStrStrMap returns an empty StrStrMap object.
|
||||
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewStrStrMap(unsafe...bool) *StrStrMap {
|
||||
return &StrStrMap{
|
||||
data : make(map[string]string),
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStrStrMapFrom returns a hash map from given map <data>.
|
||||
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStrStrMapFrom(data map[string]string, unsafe...bool) *StrStrMap {
|
||||
return &StrStrMap{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
data : data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *StrStrMap) Iterator(f func (k string, v string) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.data {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *StrStrMap) Clone() *StrStrMap {
|
||||
return NewStrStrMapFrom(m.Map(), !m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (m *StrStrMap) Map() map[string]string {
|
||||
m.mu.RLock()
|
||||
data := make(map[string]string, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *StrStrMap) Set(key string, val string) {
|
||||
m.mu.Lock()
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *StrStrMap) Sets(data map[string]string) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given <key>.
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *StrStrMap) Search(key string) (value string, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *StrStrMap) Get(key string) string {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (m *StrStrMap) doSetWithLockCheck(key string, value string) string {
|
||||
m.mu.Lock()
|
||||
if v, ok := m.data[key]; ok {
|
||||
m.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
m.data[key] = value
|
||||
m.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (m *StrStrMap) GetOrSet(key string, value string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (m *StrStrMap) GetOrSetFunc(key string, f func() string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (m *StrStrMap) GetOrSetFuncLock(key string, f func() string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
v = f()
|
||||
m.data[key] = v
|
||||
return v
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *StrStrMap) SetIfNotExist(key string, value string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *StrStrMap) SetIfNotExistFunc(key string, f func() string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (m *StrStrMap) SetIfNotExistFuncLock(key string, f func() string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *StrStrMap) Removes(keys []string) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *StrStrMap) Remove(key string) string {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *StrStrMap) Keys() []string {
|
||||
m.mu.RLock()
|
||||
keys := make([]string, len(m.data))
|
||||
index := 0
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *StrStrMap) Values() []string {
|
||||
m.mu.RLock()
|
||||
values := make([]string, len(m.data))
|
||||
index := 0
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *StrStrMap) Contains(key string) bool {
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *StrStrMap) Size() int {
|
||||
m.mu.RLock()
|
||||
length := len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *StrStrMap) IsEmpty() bool {
|
||||
m.mu.RLock()
|
||||
empty := len(m.data) == 0
|
||||
m.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *StrStrMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[string]string)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (m *StrStrMap) LockFunc(f func(m map[string]string)) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
|
||||
func (m *StrStrMap) RLockFunc(f func(m map[string]string)) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
f(m.data)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *StrStrMap) Flip() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
n := make(map[string]string, len(m.data))
|
||||
for k, v := range m.data {
|
||||
n[v] = k
|
||||
}
|
||||
m.data = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <m>.
|
||||
func (m *StrStrMap) Merge(other *StrStrMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
@ -1,305 +0,0 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type IntBoolMap struct {
|
||||
m map[int]bool
|
||||
mu *rwmutex.RWMutex
|
||||
}
|
||||
|
||||
// NewIntBoolMap returns an empty IntBoolMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewIntBoolMap(unsafe...bool) *IntBoolMap {
|
||||
return &IntBoolMap{
|
||||
m : make(map[int]bool),
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntBoolMapFrom returns an IntBoolMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntBoolMapFrom(m map[int]bool, unsafe...bool) *IntBoolMap {
|
||||
return &IntBoolMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntBoolMapFromArray returns an IntBoolMap from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewIntBoolMapFromArray(keys []int, values []bool, unsafe...bool) *IntBoolMap {
|
||||
m := make(map[int]bool)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = false
|
||||
}
|
||||
}
|
||||
return &IntBoolMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *IntBoolMap) Clone() *IntBoolMap {
|
||||
return NewIntBoolMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *IntBoolMap) Map() map[int]bool {
|
||||
m := make(map[int]bool)
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *IntBoolMap) Iterator(f func (k int, v bool) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *IntBoolMap) Set(key int, val bool) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *IntBoolMap) BatchSet(m map[int]bool) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *IntBoolMap) Get(key int) bool {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *IntBoolMap) doSetWithLockCheck(key int, value bool) bool {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *IntBoolMap) GetOrSet(key int, value bool) bool {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
func (gm *IntBoolMap) GetOrSetFunc(key int, f func() bool) bool {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *IntBoolMap) GetOrSetFuncLock(key int, f func() bool) bool {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntBoolMap) SetIfNotExist(key int, value bool) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntBoolMap) SetIfNotExistFunc(key int, f func() bool) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *IntBoolMap) SetIfNotExistFuncLock(key int, f func() bool) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if _, ok := gm.m[key]; !ok {
|
||||
gm.m[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *IntBoolMap) BatchRemove(keys []int) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *IntBoolMap) Remove(key int) bool {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *IntBoolMap) Keys() []int {
|
||||
gm.mu.RLock()
|
||||
keys := make([]int, 0)
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *IntBoolMap) Contains(key int) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (gm *IntBoolMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *IntBoolMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *IntBoolMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[int]bool)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *IntBoolMap) LockFunc(f func(m map[int]bool)) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *IntBoolMap) RLockFunc(f func(m map[int]bool)) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *IntBoolMap) Merge(other *IntBoolMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -1,327 +0,0 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type IntIntMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
m map[int]int
|
||||
}
|
||||
|
||||
// NewIntIntMap returns an empty IntIntMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewIntIntMap(unsafe...bool) *IntIntMap {
|
||||
return &IntIntMap{
|
||||
m : make(map[int]int),
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntIntMapFrom returns an IntIntMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntIntMapFrom(m map[int]int, unsafe...bool) *IntIntMap {
|
||||
return &IntIntMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntIntMapFromArray returns an IntIntMap object from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewIntIntMapFromArray(keys []int, values []int, unsafe...bool) *IntIntMap {
|
||||
m := make(map[int]int)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = 0
|
||||
}
|
||||
}
|
||||
return &IntIntMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *IntIntMap) Iterator(f func (k int, v int) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *IntIntMap) Clone() *IntIntMap {
|
||||
return NewIntIntMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *IntIntMap) Map() map[int]int {
|
||||
m := make(map[int]int)
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *IntIntMap) Set(key int, val int) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *IntIntMap) BatchSet(m map[int]int) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *IntIntMap) Get(key int) (int) {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *IntIntMap) doSetWithLockCheck(key int, value int) int {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *IntIntMap) GetOrSet(key int, value int) int {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
func (gm *IntIntMap) GetOrSetFunc(key int, f func() int) int {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntIntMap) SetIfNotExist(key int, value int) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntIntMap) SetIfNotExistFunc(key int, f func() int) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if _, ok := gm.m[key]; !ok {
|
||||
gm.m[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *IntIntMap) BatchRemove(keys []int) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *IntIntMap) Remove(key int) int {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *IntIntMap) Keys() []int {
|
||||
gm.mu.RLock()
|
||||
keys := make([]int, 0)
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (gm *IntIntMap) Values() []int {
|
||||
gm.mu.RLock()
|
||||
vals := make([]int, 0)
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *IntIntMap) Contains(key int) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (gm *IntIntMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *IntIntMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *IntIntMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[int]int)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *IntIntMap) LockFunc(f func(m map[int]int)) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *IntIntMap) RLockFunc(f func(m map[int]int)) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
func (gm *IntIntMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[int]int, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[v] = k
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *IntIntMap) Merge(other *IntIntMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -1,318 +0,0 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type IntInterfaceMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
m map[int]interface{}
|
||||
}
|
||||
|
||||
// NewIntInterfaceMap returns an empty IntInterfaceMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewIntInterfaceMap(unsafe...bool) *IntInterfaceMap {
|
||||
return &IntInterfaceMap{
|
||||
m : make(map[int]interface{}),
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntInterfaceMapFrom returns an IntInterfaceMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntInterfaceMapFrom(m map[int]interface{}, unsafe...bool) *IntInterfaceMap {
|
||||
return &IntInterfaceMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewFromArray returns a hash map from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewIntInterfaceMapFromArray(keys []int, values []interface{}, unsafe...bool) *IntInterfaceMap {
|
||||
m := make(map[int]interface{})
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = interface{}(nil)
|
||||
}
|
||||
}
|
||||
return &IntInterfaceMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *IntInterfaceMap) Iterator(f func (k int, v interface{}) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *IntInterfaceMap) Clone() *IntInterfaceMap {
|
||||
return NewIntInterfaceMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *IntInterfaceMap) Map() map[int]interface{} {
|
||||
m := make(map[int]interface{})
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *IntInterfaceMap) Set(key int, val interface{}) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *IntInterfaceMap) BatchSet(m map[int]interface{}) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *IntInterfaceMap) Get(key int) (interface{}) {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the hash map,
|
||||
// and its return value will be set to the map with <key>.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *IntInterfaceMap) doSetWithLockCheck(key int, value interface{}) interface{} {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface {}); ok {
|
||||
value = f()
|
||||
}
|
||||
if value != nil {
|
||||
gm.m[key] = value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *IntInterfaceMap) GetOrSet(key int, value interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
func (gm *IntInterfaceMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *IntInterfaceMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntInterfaceMap) SetIfNotExist(key int, value interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntInterfaceMap) SetIfNotExistFunc(key int, f func() interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *IntInterfaceMap) SetIfNotExistFuncLock(key int, f func() interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *IntInterfaceMap) BatchRemove(keys []int) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *IntInterfaceMap) Remove(key int) interface{} {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *IntInterfaceMap) Keys() []int {
|
||||
gm.mu.RLock()
|
||||
keys := make([]int, 0)
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (gm *IntInterfaceMap) Values() []interface{} {
|
||||
gm.mu.RLock()
|
||||
vals := make([]interface{}, 0)
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *IntInterfaceMap) Contains(key int) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (gm *IntInterfaceMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *IntInterfaceMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *IntInterfaceMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[int]interface{})
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *IntInterfaceMap) LockFunc(f func(m map[int]interface{})) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *IntInterfaceMap) RLockFunc(f func(m map[int]interface{})) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
func (gm *IntInterfaceMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[int]interface{}, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[gconv.Int(v)] = k
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *IntInterfaceMap) Merge(other *IntInterfaceMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -1,327 +0,0 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type IntStringMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
m map[int]string
|
||||
}
|
||||
|
||||
// NewIntStringMap returns an empty IntStringMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewIntStringMap(unsafe ...bool) *IntStringMap {
|
||||
return &IntStringMap{
|
||||
m: make(map[int]string),
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntStringMapFrom returns an IntStringMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntStringMapFrom(m map[int]string, unsafe ...bool) *IntStringMap {
|
||||
return &IntStringMap{
|
||||
m: m,
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntStringMapFromArray returns an IntStringMap object from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewIntStringMapFromArray(keys []int, values []string, unsafe ...bool) *IntStringMap {
|
||||
m := make(map[int]string)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = ""
|
||||
}
|
||||
}
|
||||
return &IntStringMap{
|
||||
m: m,
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *IntStringMap) Iterator(f func(k int, v string) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *IntStringMap) Clone() *IntStringMap {
|
||||
return NewIntStringMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *IntStringMap) Map() map[int]string {
|
||||
m := make(map[int]string)
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *IntStringMap) Set(key int, val string) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *IntStringMap) BatchSet(m map[int]string) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *IntStringMap) Get(key int) string {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *IntStringMap) doSetWithLockCheck(key int, value string) string {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *IntStringMap) GetOrSet(key int, value string) string {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
func (gm *IntStringMap) GetOrSetFunc(key int, f func() string) string {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *IntStringMap) GetOrSetFuncLock(key int, f func() string) string {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntStringMap) SetIfNotExist(key int, value string) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *IntStringMap) SetIfNotExistFunc(key int, f func() string) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *IntStringMap) SetIfNotExistFuncLock(key int, f func() string) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if _, ok := gm.m[key]; !ok {
|
||||
gm.m[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *IntStringMap) BatchRemove(keys []int) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *IntStringMap) Remove(key int) string {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *IntStringMap) Keys() []int {
|
||||
gm.mu.RLock()
|
||||
keys := make([]int, 0)
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (gm *IntStringMap) Values() []string {
|
||||
gm.mu.RLock()
|
||||
vals := make([]string, 0)
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *IntStringMap) Contains(key int) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (gm *IntStringMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *IntStringMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *IntStringMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[int]string)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *IntStringMap) LockFunc(f func(m map[int]string)) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *IntStringMap) RLockFunc(f func(m map[int]string)) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
func (gm *IntStringMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[int]string, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[gconv.Int(v)] = gconv.String(k)
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *IntStringMap) Merge(other *IntStringMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
366
g/container/gmap/gmap_link_map.go
Normal file
366
g/container/gmap/gmap_link_map.go
Normal file
@ -0,0 +1,366 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/glist"
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type ListMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
data map[interface{}]*glist.Element
|
||||
list *glist.List
|
||||
}
|
||||
|
||||
type gListMapNode struct {
|
||||
key interface{}
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// NewListMap returns an empty link map.
|
||||
// ListMap is backed by a hash table to store values and doubly-linked list to store ordering.
|
||||
// The param <unsafe> used to specify whether using map in un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewListMap(unsafe ...bool) *ListMap {
|
||||
return &ListMap{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
data : make(map[interface{}]*glist.Element),
|
||||
list : glist.New(true),
|
||||
}
|
||||
}
|
||||
|
||||
// NewListMapFrom returns a link map from given map <data>.
|
||||
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewListMapFrom(data map[interface{}]interface{}, unsafe...bool) *ListMap {
|
||||
m := NewListMap(unsafe...)
|
||||
m.Sets(data)
|
||||
return m
|
||||
}
|
||||
|
||||
// Iterator is alias of IteratorAsc.
|
||||
func (m *ListMap) Iterator(f func (key, value interface{}) bool) {
|
||||
m.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the map in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *ListMap) IteratorAsc(f func (key interface{}, value interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
node := (*gListMapNode)(nil)
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
})
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the map in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *ListMap) IteratorDesc(f func (key interface{}, value interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
node := (*gListMapNode)(nil)
|
||||
m.list.IteratorDesc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
})
|
||||
}
|
||||
|
||||
// Clone returns a new link map with copy of current map data.
|
||||
func (m *ListMap) Clone(unsafe ...bool) *ListMap {
|
||||
return NewListMapFrom(m.Map(), unsafe ...)
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
func (m *ListMap) Clear() {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New(true)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the map.
|
||||
func (m *ListMap) Map() map[interface{}]interface{} {
|
||||
m.mu.RLock()
|
||||
node := (*gListMapNode)(nil)
|
||||
data := make(map[interface{}]interface{}, len(m.data))
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
data[node.key] = node.value
|
||||
return true
|
||||
})
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// Set sets key-value to the map.
|
||||
func (m *ListMap) Set(key interface{}, value interface{}) {
|
||||
m.mu.Lock()
|
||||
if e, ok := m.data[key]; !ok {
|
||||
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
|
||||
} else {
|
||||
e.Value = &gListMapNode{key, value}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the map.
|
||||
func (m *ListMap) Sets(data map[interface{}]interface{}) {
|
||||
m.mu.Lock()
|
||||
for key, value := range data {
|
||||
if e, ok := m.data[key]; !ok {
|
||||
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
|
||||
} else {
|
||||
e.Value = &gListMapNode{key, value}
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Search searches the map with given <key>.
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *ListMap) Search(key interface{}) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
found = ok
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *ListMap) Get(key interface{}) (value interface{}) {
|
||||
m.mu.RLock()
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the map,
|
||||
// and its return value will be set to the map with <key>.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (m *ListMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if e, ok := m.data[key]; ok {
|
||||
return e.Value.(*gListMapNode).value
|
||||
}
|
||||
if f, ok := value.(func() interface {}); ok {
|
||||
value = f()
|
||||
}
|
||||
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (m *ListMap) GetOrSet(key interface{}, value interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (m *ListMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the map.
|
||||
func (m *ListMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
return m.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with the value by given <key>.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVar(key interface{}) *gvar.Var {
|
||||
return gvar.New(m.Get(key), true)
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value), true)
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f), true)
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f), true)
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *ListMap) SetIfNotExist(key interface{}, value interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (m *ListMap) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the map.
|
||||
func (m *ListMap) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
|
||||
if !m.Contains(key) {
|
||||
m.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *ListMap) Remove(key interface{}) (value interface{}) {
|
||||
m.mu.Lock()
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *ListMap) Removes(keys []interface{}) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
if e, ok := m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice in ascending order.
|
||||
func (m *ListMap) Keys() []interface{} {
|
||||
m.mu.RLock()
|
||||
keys := make([]interface{}, m.list.Len())
|
||||
index := 0
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
keys[index] = e.Value.(*gListMapNode).key
|
||||
index++
|
||||
return true
|
||||
})
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *ListMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
values := make([]interface{}, m.list.Len())
|
||||
index := 0
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
values[index] = e.Value.(*gListMapNode).value
|
||||
index++
|
||||
return true
|
||||
})
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *ListMap) Contains(key interface{}) (ok bool) {
|
||||
m.mu.RLock()
|
||||
_, ok = m.data[key]
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (m *ListMap) Size() (size int) {
|
||||
m.mu.RLock()
|
||||
size = len(m.data)
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *ListMap) IsEmpty() bool {
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map to value-key.
|
||||
func (m *ListMap) Flip() {
|
||||
data := m.Map()
|
||||
m.Clear()
|
||||
for key, value := range data {
|
||||
m.Set(value, key)
|
||||
}
|
||||
}
|
||||
|
||||
// Merge merges two link maps.
|
||||
// The <other> map will be merged into the map <m>.
|
||||
func (m *ListMap) Merge(other *ListMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
node := (*gListMapNode)(nil)
|
||||
other.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
if e, ok := m.data[node.key]; !ok {
|
||||
m.data[node.key] = m.list.PushBack(&gListMapNode{node.key, node.value})
|
||||
} else {
|
||||
e.Value = &gListMapNode{node.key, node.value}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
@ -1,308 +0,0 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type StringBoolMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
m map[string]bool
|
||||
}
|
||||
|
||||
// NewStringBoolMap returns an empty StringBoolMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewStringBoolMap(unsafe...bool) *StringBoolMap {
|
||||
return &StringBoolMap{
|
||||
m : make(map[string]bool),
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStringBoolMapFrom returns an StringBoolMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStringBoolMapFrom(m map[string]bool, unsafe...bool) *StringBoolMap {
|
||||
return &StringBoolMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewFromArray returns a hash map from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewStringBoolMapFromArray(keys []string, values []bool, unsafe...bool) *StringBoolMap {
|
||||
m := make(map[string]bool)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = false
|
||||
}
|
||||
}
|
||||
return &StringBoolMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *StringBoolMap) Iterator(f func (k string, v bool) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *StringBoolMap) Clone() *StringBoolMap {
|
||||
return NewStringBoolMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *StringBoolMap) Map() map[string]bool {
|
||||
m := make(map[string]bool)
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *StringBoolMap) Set(key string, val bool) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *StringBoolMap) BatchSet(m map[string]bool) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *StringBoolMap) Get(key string) bool {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *StringBoolMap) doSetWithLockCheck(key string, value bool) bool {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *StringBoolMap) GetOrSet(key string, value bool) bool {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (gm *StringBoolMap) GetOrSetFunc(key string, f func() bool) bool {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *StringBoolMap) GetOrSetFuncLock(key string, f func() bool) bool {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringBoolMap) SetIfNotExist(key string, value bool) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringBoolMap) SetIfNotExistFunc(key string, f func() bool) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *StringBoolMap) SetIfNotExistFuncLock(key string, f func() bool) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if _, ok := gm.m[key]; !ok {
|
||||
gm.m[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *StringBoolMap) BatchRemove(keys []string) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *StringBoolMap) Remove(key string) bool {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *StringBoolMap) Keys() []string {
|
||||
gm.mu.RLock()
|
||||
keys := make([]string, 0)
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *StringBoolMap) Contains(key string) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (gm *StringBoolMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *StringBoolMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *StringBoolMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[string]bool)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *StringBoolMap) LockFunc(f func(m map[string]bool)) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *StringBoolMap) RLockFunc(f func(m map[string]bool)) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *StringBoolMap) Merge(other *StringBoolMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -1,330 +0,0 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type StringIntMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
m map[string]int
|
||||
}
|
||||
|
||||
// NewStringIntMap returns an empty StringIntMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewStringIntMap(unsafe ...bool) *StringIntMap {
|
||||
return &StringIntMap{
|
||||
m: make(map[string]int),
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStringIntMapFrom returns an StringIntMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStringIntMapFrom(m map[string]int, unsafe ...bool) *StringIntMap {
|
||||
return &StringIntMap{
|
||||
m: m,
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStringIntMapFromArray returns an StringIntMap object from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewStringIntMapFromArray(keys []string, values []int, unsafe ...bool) *StringIntMap {
|
||||
m := make(map[string]int)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = 0
|
||||
}
|
||||
}
|
||||
return &StringIntMap{
|
||||
m: m,
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *StringIntMap) Iterator(f func(k string, v int) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *StringIntMap) Clone() *StringIntMap {
|
||||
return NewStringIntMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *StringIntMap) Map() map[string]int {
|
||||
m := make(map[string]int)
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *StringIntMap) Set(key string, val int) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *StringIntMap) BatchSet(m map[string]int) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *StringIntMap) Get(key string) int {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *StringIntMap) doSetWithLockCheck(key string, value int) int {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *StringIntMap) GetOrSet(key string, value int) int {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (gm *StringIntMap) GetOrSetFunc(key string, f func() int) int {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *StringIntMap) GetOrSetFuncLock(key string, f func() int) int {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringIntMap) SetIfNotExist(key string, value int) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringIntMap) SetIfNotExistFunc(key string, f func() int) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *StringIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if _, ok := gm.m[key]; !ok {
|
||||
gm.m[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *StringIntMap) BatchRemove(keys []string) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *StringIntMap) Remove(key string) int {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *StringIntMap) Keys() []string {
|
||||
gm.mu.RLock()
|
||||
keys := make([]string, 0)
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (gm *StringIntMap) Values() []int {
|
||||
gm.mu.RLock()
|
||||
vals := make([]int, 0)
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *StringIntMap) Contains(key string) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (gm *StringIntMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *StringIntMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *StringIntMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[string]int)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *StringIntMap) LockFunc(f func(m map[string]int)) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *StringIntMap) RLockFunc(f func(m map[string]int)) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
func (gm *StringIntMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[string]int, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[gconv.String(v)] = gconv.Int(k)
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *StringIntMap) Merge(other *StringIntMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -1,318 +0,0 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type StringInterfaceMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
m map[string]interface{}
|
||||
}
|
||||
|
||||
// NewStringInterfaceMap returns an empty StringInterfaceMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewStringInterfaceMap(unsafe ...bool) *StringInterfaceMap {
|
||||
return &StringInterfaceMap{
|
||||
m: make(map[string]interface{}),
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStringInterfaceMapFrom returns an StringInterfaceMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStringInterfaceMapFrom(m map[string]interface{}, unsafe ...bool) *StringInterfaceMap {
|
||||
return &StringInterfaceMap{
|
||||
m: m,
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStringInterfaceMapFromArray returns an StringInterfaceMap object from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewStringInterfaceMapFromArray(keys []string, values []interface{}, unsafe ...bool) *StringInterfaceMap {
|
||||
m := make(map[string]interface{})
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = interface{}(nil)
|
||||
}
|
||||
}
|
||||
return &StringInterfaceMap{
|
||||
m: m,
|
||||
mu: rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *StringInterfaceMap) Iterator(f func(k string, v interface{}) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *StringInterfaceMap) Clone() *StringInterfaceMap {
|
||||
return NewStringInterfaceMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *StringInterfaceMap) Map() map[string]interface{} {
|
||||
m := make(map[string]interface{})
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *StringInterfaceMap) Set(key string, val interface{}) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *StringInterfaceMap) BatchSet(m map[string]interface{}) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *StringInterfaceMap) Get(key string) interface{} {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the hash map,
|
||||
// and its return value will be set to the map with <key>.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *StringInterfaceMap) doSetWithLockCheck(key string, value interface{}) interface{} {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface{}); ok {
|
||||
value = f()
|
||||
}
|
||||
if value != nil {
|
||||
gm.m[key] = value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *StringInterfaceMap) GetOrSet(key string, value interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (gm *StringInterfaceMap) GetOrSetFunc(key string, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *StringInterfaceMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringInterfaceMap) SetIfNotExist(key string, value interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringInterfaceMap) SetIfNotExistFunc(key string, f func() interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *StringInterfaceMap) SetIfNotExistFuncLock(key string, f func() interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *StringInterfaceMap) BatchRemove(keys []string) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *StringInterfaceMap) Remove(key string) interface{} {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *StringInterfaceMap) Keys() []string {
|
||||
gm.mu.RLock()
|
||||
keys := make([]string, 0)
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (gm *StringInterfaceMap) Values() []interface{} {
|
||||
gm.mu.RLock()
|
||||
vals := make([]interface{}, 0)
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *StringInterfaceMap) Contains(key string) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (gm *StringInterfaceMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *StringInterfaceMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *StringInterfaceMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[string]interface{})
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *StringInterfaceMap) LockFunc(f func(m map[string]interface{})) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *StringInterfaceMap) RLockFunc(f func(m map[string]interface{})) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
func (gm *StringInterfaceMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[string]interface{}, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[gconv.String(v)] = k
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *StringInterfaceMap) Merge(other *StringInterfaceMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -1,329 +0,0 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type StringStringMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
m map[string]string
|
||||
}
|
||||
|
||||
// NewStringStringMap returns an empty StringStringMap object.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func NewStringStringMap(unsafe...bool) *StringStringMap {
|
||||
return &StringStringMap{
|
||||
m : make(map[string]string),
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStringStringMapFrom returns an StringStringMap object from given map <m>.
|
||||
// Notice that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStringStringMapFrom(m map[string]string, unsafe...bool) *StringStringMap {
|
||||
return &StringStringMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStringStringMapFromArray returns an StringStringMap object from given array.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as its corresponding values.
|
||||
//
|
||||
// If length of <keys> is greater than that of <values>,
|
||||
// the corresponding overflow map values will be the default value of its type.
|
||||
func NewStringStringMapFromArray(keys []string, values []string, unsafe...bool) *StringStringMap {
|
||||
m := make(map[string]string)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = ""
|
||||
}
|
||||
}
|
||||
return &StringStringMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
func (gm *StringStringMap) Iterator(f func (k string, v string) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (gm *StringStringMap) Clone() *StringStringMap {
|
||||
return NewStringStringMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the hash map.
|
||||
func (gm *StringStringMap) Map() map[string]string {
|
||||
m := make(map[string]string)
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (gm *StringStringMap) Set(key string, val string) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// BatchSet batch sets key-values to the hash map.
|
||||
func (gm *StringStringMap) BatchSet(m map[string]string) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (gm *StringStringMap) Get(key string) string {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (gm *StringStringMap) doSetWithLockCheck(key string, value string) string {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (gm *StringStringMap) GetOrSet(key string, value string) string {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (gm *StringStringMap) GetOrSetFunc(key string, f func() string) string {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (gm *StringStringMap) GetOrSetFuncLock(key string, f func() string) string {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringStringMap) SetIfNotExist(key string, value string) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (gm *StringStringMap) SetIfNotExistFunc(key string, f func() string) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (gm *StringStringMap) SetIfNotExistFuncLock(key string, f func() string) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if _, ok := gm.m[key]; !ok {
|
||||
gm.m[key] = f()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BatchRemove batch deletes values of the map by keys.
|
||||
func (gm *StringStringMap) BatchRemove(keys []string) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (gm *StringStringMap) Remove(key string) string {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (gm *StringStringMap) Keys() []string {
|
||||
gm.mu.RLock()
|
||||
keys := make([]string, 0)
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (gm *StringStringMap) Values() []string {
|
||||
gm.mu.RLock()
|
||||
vals := make([]string, 0)
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (gm *StringStringMap) Contains(key string) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
func (gm *StringStringMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (gm *StringStringMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying map data map.
|
||||
func (gm *StringStringMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[string]string)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> and mutex.Lock.
|
||||
func (gm *StringStringMap) LockFunc(f func(m map[string]string)) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// RLockFunc locks reading with given callback function <f> and mutex.RLock.
|
||||
func (gm *StringStringMap) RLockFunc(f func(m map[string]string)) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
func (gm *StringStringMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[string]string, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[v] = k
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// Merge merges two hash maps.
|
||||
// The <other> map will be merged into the map <gm>.
|
||||
func (gm *StringStringMap) Merge(other *StringStringMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if other != gm {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
for k, v := range other.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
30
g/container/gmap/gmap_tree_map.go
Normal file
30
g/container/gmap/gmap_tree_map.go
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gtree"
|
||||
)
|
||||
|
||||
// Map based on red-black tree, alias of RedBlackTree.
|
||||
type TreeMap = gtree.RedBlackTree
|
||||
|
||||
// NewTreeMap instantiates a tree map with the custom comparator.
|
||||
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewTreeMap(comparator func(v1, v2 interface{}) int, unsafe...bool) *TreeMap {
|
||||
return gtree.NewRedBlackTree(comparator, unsafe...)
|
||||
}
|
||||
|
||||
// NewTreeMapFrom instantiates a tree map with the custom comparator and <data> map.
|
||||
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewTreeMapFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe...bool) *TreeMap {
|
||||
return gtree.NewRedBlackTreeFrom(comparator, data, unsafe...)
|
||||
}
|
||||
@ -1,3 +1,9 @@
|
||||
// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
@ -9,9 +15,7 @@ import (
|
||||
func getValue() interface{} {
|
||||
return 3
|
||||
}
|
||||
func callBack(k interface{}, v interface{}) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func Test_Map_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.New()
|
||||
@ -44,9 +48,6 @@ func Test_Map_Basic(t *testing.T) {
|
||||
|
||||
m2 := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
m3 := gmap.NewFromArray([]interface{}{1, "key1"}, []interface{}{1, "val1"})
|
||||
gtest.Assert(m3.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_Map_Set_Fun(t *testing.T) {
|
||||
@ -62,12 +63,45 @@ func Test_Map_Set_Fun(t *testing.T) {
|
||||
|
||||
func Test_Map_Batch(t *testing.T) {
|
||||
m := gmap.New()
|
||||
m.BatchSet(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
m.Iterator(callBack)
|
||||
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
m.BatchRemove([]interface{}{"key1", 1})
|
||||
m.Removes([]interface{}{"key1", 1})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
|
||||
}
|
||||
func Test_Map_Iterator(t *testing.T){
|
||||
expect :=map[interface{}]interface{}{1: 1, "key1": "val1"}
|
||||
|
||||
m := gmap.NewFrom(expect)
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, 2)
|
||||
gtest.Assert(j, 1)
|
||||
}
|
||||
|
||||
func Test_Map_Lock(t *testing.T){
|
||||
expect :=map[interface{}]interface{}{1: 1, "key1": "val1"}
|
||||
|
||||
m := gmap.NewFrom(expect)
|
||||
m.LockFunc(func(m map[interface{}]interface{}) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[interface{}]interface{}) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Map_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
55
g/container/gmap/gmap_z_bench_maps_test.go
Normal file
55
g/container/gmap/gmap_z_bench_maps_test.go
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/util/gutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var hashMap = gmap.New()
|
||||
var listMap = gmap.NewListMap()
|
||||
var treeMap = gmap.NewTreeMap(gutil.ComparatorInt)
|
||||
|
||||
func Benchmark_HashMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
hashMap.Set(i, i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_ListMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
listMap.Set(i, i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_TreeMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
treeMap.Set(i, i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_HashMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
hashMap.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_ListMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
listMap.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_TreeMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
treeMap.Get(i)
|
||||
}
|
||||
}
|
||||
@ -6,31 +6,21 @@
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
package gmap
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"testing"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
|
||||
var ibm = NewIntBoolMap()
|
||||
var iim = NewIntIntMap()
|
||||
var iifm = NewIntInterfaceMap()
|
||||
var ism = NewIntStringMap()
|
||||
var ififm = NewMap()
|
||||
var sbm = NewStringBoolMap()
|
||||
var sim = NewStringIntMap()
|
||||
var sifm = NewStringInterfaceMap()
|
||||
var ssm = NewStringStringMap()
|
||||
|
||||
// 写入性能测试
|
||||
|
||||
func Benchmark_IntBoolMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ibm.Set(i, true)
|
||||
}
|
||||
}
|
||||
var ififm = gmap.New()
|
||||
var iim = gmap.NewIntIntMap()
|
||||
var iifm = gmap.NewIntAnyMap()
|
||||
var ism = gmap.NewIntStrMap()
|
||||
var sim = gmap.NewStrIntMap()
|
||||
var sifm = gmap.NewStrAnyMap()
|
||||
var ssm = gmap.NewStrStrMap()
|
||||
|
||||
func Benchmark_IntIntMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
@ -38,56 +28,43 @@ func Benchmark_IntIntMap_Set(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_IntInterfaceMap_Set(b *testing.B) {
|
||||
func Benchmark_IntAnyMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
iifm.Set(i, i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_IntStringMap_Set(b *testing.B) {
|
||||
func Benchmark_IntStrMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ism.Set(i, strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_InterfaceInterfaceMap_Set(b *testing.B) {
|
||||
func Benchmark_AnyAnyMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ififm.Set(i, i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_StringBoolMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sbm.Set(strconv.Itoa(i), true)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_StringIntMap_Set(b *testing.B) {
|
||||
func Benchmark_StrIntMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sim.Set(strconv.Itoa(i), i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_StringInterfaceMap_Set(b *testing.B) {
|
||||
func Benchmark_StrAnyMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sifm.Set(strconv.Itoa(i), i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_StringStringMap_Set(b *testing.B) {
|
||||
func Benchmark_StrStrMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ssm.Set(strconv.Itoa(i), strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 读取性能测试
|
||||
|
||||
func Benchmark_IntBoolMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ibm.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_IntIntMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
@ -95,43 +72,37 @@ func Benchmark_IntIntMap_Get(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_IntInterfaceMap_Get(b *testing.B) {
|
||||
func Benchmark_IntAnyMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
iifm.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_IntStringMap_Get(b *testing.B) {
|
||||
func Benchmark_IntStrMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ism.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_InterfaceInterfaceMap_Get(b *testing.B) {
|
||||
func Benchmark_AnyAnyMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ififm.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_StringBoolMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sbm.Get(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_StringIntMap_Get(b *testing.B) {
|
||||
func Benchmark_StrIntMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sim.Get(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_StringInterfaceMap_Get(b *testing.B) {
|
||||
func Benchmark_StrAnyMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sifm.Get(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_StringStringMap_Get(b *testing.B) {
|
||||
func Benchmark_StrStrMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ssm.Get(strconv.Itoa(i))
|
||||
}
|
||||
|
||||
@ -6,75 +6,61 @@
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
package gmap
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"testing"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
|
||||
var ibmUnsafe = NewIntBoolMap(true)
|
||||
var iimUnsafe = NewIntIntMap(true)
|
||||
var iifmUnsafe = NewIntInterfaceMap(true)
|
||||
var ismUnsafe = NewIntStringMap(true)
|
||||
var ififmUnsafe = NewMap(true)
|
||||
var sbmUnsafe = NewStringBoolMap(true)
|
||||
var simUnsafe = NewStringIntMap(true)
|
||||
var sifmUnsafe = NewStringInterfaceMap(true)
|
||||
var ssmUnsafe = NewStringStringMap(true)
|
||||
var ififmUnsafe = gmap.New(true)
|
||||
var iimUnsafe = gmap.NewIntIntMap(true)
|
||||
var iifmUnsafe = gmap.NewIntAnyMap(true)
|
||||
var ismUnsafe = gmap.NewIntStrMap(true)
|
||||
var simUnsafe = gmap.NewStrIntMap(true)
|
||||
var sifmUnsafe = gmap.NewStrAnyMap(true)
|
||||
var ssmUnsafe = gmap.NewStrStrMap(true)
|
||||
|
||||
// 写入性能测试
|
||||
|
||||
func Benchmark_Unsafe_IntBoolMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ibmUnsafe.Set(i, true)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntIntMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
iimUnsafe.Set(i, i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntInterfaceMap_Set(b *testing.B) {
|
||||
func Benchmark_Unsafe_IntAnyMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
iifmUnsafe.Set(i, i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntStringMap_Set(b *testing.B) {
|
||||
func Benchmark_Unsafe_IntStrMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ismUnsafe.Set(i, strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_InterfaceInterfaceMap_Set(b *testing.B) {
|
||||
func Benchmark_Unsafe_AnyAnyMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ififmUnsafe.Set(i, i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringBoolMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sbmUnsafe.Set(strconv.Itoa(i), true)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringIntMap_Set(b *testing.B) {
|
||||
func Benchmark_Unsafe_StrIntMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
simUnsafe.Set(strconv.Itoa(i), i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringInterfaceMap_Set(b *testing.B) {
|
||||
func Benchmark_Unsafe_StrAnyMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sifmUnsafe.Set(strconv.Itoa(i), i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringStringMap_Set(b *testing.B) {
|
||||
func Benchmark_Unsafe_StrStrMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ssmUnsafe.Set(strconv.Itoa(i), strconv.Itoa(i))
|
||||
}
|
||||
@ -83,11 +69,6 @@ func Benchmark_Unsafe_StringStringMap_Set(b *testing.B) {
|
||||
|
||||
// 读取性能测试
|
||||
|
||||
func Benchmark_Unsafe_IntBoolMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ibmUnsafe.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntIntMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
@ -95,43 +76,37 @@ func Benchmark_Unsafe_IntIntMap_Get(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntInterfaceMap_Get(b *testing.B) {
|
||||
func Benchmark_Unsafe_IntAnyMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
iifmUnsafe.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntStringMap_Get(b *testing.B) {
|
||||
func Benchmark_Unsafe_IntStrMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ismUnsafe.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_InterfaceInterfaceMap_Get(b *testing.B) {
|
||||
func Benchmark_Unsafe_AnyAnyMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ififmUnsafe.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringBoolMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sbmUnsafe.Get(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringIntMap_Get(b *testing.B) {
|
||||
func Benchmark_Unsafe_StrIntMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
simUnsafe.Get(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringInterfaceMap_Get(b *testing.B) {
|
||||
func Benchmark_Unsafe_StrAnyMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sifmUnsafe.Get(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringStringMap_Get(b *testing.B) {
|
||||
func Benchmark_Unsafe_StrStrMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ssmUnsafe.Get(strconv.Itoa(i))
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ func Example_Normal_Basic() {
|
||||
fmt.Println(m.Values())
|
||||
|
||||
//Batch add data
|
||||
m.BatchSet(add_map)
|
||||
m.Sets(add_map)
|
||||
|
||||
//Gets the value of the corresponding key
|
||||
key3_val := m.Get("key3")
|
||||
@ -43,7 +43,7 @@ func Example_Normal_Basic() {
|
||||
|
||||
//Batch remove keys
|
||||
remove_keys := []interface{}{"key1", 1}
|
||||
m.BatchRemove(remove_keys)
|
||||
m.Removes(remove_keys)
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
//Contains checks whether a key exists.
|
||||
131
g/container/gmap/gmap_z_int_any_test.go
Normal file
131
g/container/gmap/gmap_z_int_any_test.go
Normal file
@ -0,0 +1,131 @@
|
||||
// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getAny() interface{} {
|
||||
return 123
|
||||
}
|
||||
func intAnyCallBack(int, interface{}) bool {
|
||||
return true
|
||||
}
|
||||
func Test_IntAnyMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, 1)
|
||||
|
||||
gtest.Assert(m.Get(1), 1)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet(2, "2"), "2")
|
||||
gtest.Assert(m.SetIfNotExist(2, "2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
gtest.Assert(m.Remove(2), "2")
|
||||
gtest.Assert(m.Contains(2), false)
|
||||
|
||||
gtest.AssertIN(3, m.Keys())
|
||||
gtest.AssertIN(1, m.Keys())
|
||||
gtest.AssertIN(3, m.Values())
|
||||
gtest.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
gtest.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntAnyMapFrom(map[int]interface{}{1: 1, 2: "2"})
|
||||
gtest.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
})
|
||||
}
|
||||
func Test_IntAnyMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
|
||||
m.GetOrSetFunc(1, getAny)
|
||||
m.GetOrSetFuncLock(2, getAny)
|
||||
gtest.Assert(m.Get(1), 123)
|
||||
gtest.Assert(m.Get(2), 123)
|
||||
|
||||
gtest.Assert(m.SetIfNotExistFunc(1, getAny), false)
|
||||
gtest.Assert(m.SetIfNotExistFunc(3, getAny), true)
|
||||
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(2, getAny), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(4, getAny), true)
|
||||
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Batch(t *testing.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
|
||||
m.Sets(map[int]interface{}{1: 1, 2: "2", 3: 3})
|
||||
gtest.Assert(m.Map(), map[int]interface{}{1: 1, 2: "2", 3: 3})
|
||||
m.Removes([]int{1, 2})
|
||||
gtest.Assert(m.Map(), map[int]interface{}{3: 3})
|
||||
}
|
||||
func Test_IntAnyMap_Iterator(t *testing.T){
|
||||
expect := map[int]interface{}{1: 1, 2: "2"}
|
||||
m := gmap.NewIntAnyMapFrom(expect)
|
||||
m.Iterator(func(k int, v interface{}) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k int, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k int, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, "2")
|
||||
gtest.Assert(j, 1)
|
||||
|
||||
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Lock(t *testing.T){
|
||||
expect := map[int]interface{}{1: 1, 2: "2"}
|
||||
m := gmap.NewIntAnyMapFrom(expect)
|
||||
m.LockFunc(func(m map[int]interface{}) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[int]interface{}) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
}
|
||||
func Test_IntAnyMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntAnyMapFrom(map[int]interface{}{1: 1, 2: "2"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN(2, m.Keys())
|
||||
}
|
||||
func Test_IntAnyMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewIntAnyMap()
|
||||
m2 := gmap.NewIntAnyMap()
|
||||
m1.Set(1, 1)
|
||||
m2.Set(2, "2")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
}
|
||||
@ -1,87 +0,0 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getBool() bool {
|
||||
return true
|
||||
}
|
||||
func intBoolCallBack(int, bool) bool {
|
||||
return true
|
||||
}
|
||||
func Test_IntBoolMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntBoolMap()
|
||||
m.Set(1, true)
|
||||
|
||||
gtest.Assert(m.Get(1), true)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet(2, false), false)
|
||||
gtest.Assert(m.SetIfNotExist(2, false), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist(3, false), true)
|
||||
|
||||
gtest.Assert(m.Remove(2), false)
|
||||
gtest.Assert(m.Contains(2), false)
|
||||
|
||||
gtest.AssertIN(3, m.Keys())
|
||||
gtest.AssertIN(1, m.Keys())
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntBoolMapFrom(map[int]bool{1: true, 2: false})
|
||||
gtest.Assert(m2.Map(), map[int]bool{1: true, 2: false})
|
||||
m3 := gmap.NewIntBoolMapFromArray([]int{1, 2}, []bool{true, false})
|
||||
gtest.Assert(m3.Map(), map[int]bool{1: true, 2: false})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_IntBoolMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewIntBoolMap()
|
||||
|
||||
m.GetOrSetFunc(1, getBool)
|
||||
m.GetOrSetFuncLock(2, getBool)
|
||||
gtest.Assert(m.Get(1), true)
|
||||
gtest.Assert(m.Get(2), true)
|
||||
gtest.Assert(m.SetIfNotExistFunc(1, getBool), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(2, getBool), false)
|
||||
}
|
||||
|
||||
func Test_IntBoolMap_Batch(t *testing.T) {
|
||||
m := gmap.NewIntBoolMap()
|
||||
|
||||
m.BatchSet(map[int]bool{1: true, 2: false, 3: true})
|
||||
m.Iterator(intBoolCallBack)
|
||||
gtest.Assert(m.Map(), map[int]bool{1: true, 2: false, 3: true})
|
||||
m.BatchRemove([]int{1, 2})
|
||||
gtest.Assert(m.Map(), map[int]bool{3: true})
|
||||
}
|
||||
|
||||
func Test_IntBoolMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntBoolMapFrom(map[int]bool{1: true, 2: false})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN(2, m.Keys())
|
||||
}
|
||||
func Test_IntBoolMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewIntBoolMap()
|
||||
m2 := gmap.NewIntBoolMap()
|
||||
m1.Set(1, true)
|
||||
m2.Set(2, false)
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[int]bool{1: true, 2: false})
|
||||
}
|
||||
@ -1,3 +1,9 @@
|
||||
// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
@ -42,9 +48,6 @@ func Test_IntIntMap_Basic(t *testing.T) {
|
||||
|
||||
m2 := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
|
||||
gtest.Assert(m2.Map(), map[int]int{1: 1, 2: 2})
|
||||
m3 := gmap.NewIntIntMapFromArray([]int{1, 2}, []int{1, 2})
|
||||
gtest.Assert(m3.Map(), map[int]int{1: 1, 2: 2})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_IntIntMap_Set_Fun(t *testing.T) {
|
||||
@ -55,19 +58,56 @@ func Test_IntIntMap_Set_Fun(t *testing.T) {
|
||||
gtest.Assert(m.Get(1), 123)
|
||||
gtest.Assert(m.Get(2), 123)
|
||||
gtest.Assert(m.SetIfNotExistFunc(1, getInt), false)
|
||||
gtest.Assert(m.SetIfNotExistFunc(3, getInt), true)
|
||||
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(2, getInt), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(4, getInt), true)
|
||||
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Batch(t *testing.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
m.BatchSet(map[int]int{1: 1, 2: 2, 3: 3})
|
||||
m.Sets(map[int]int{1: 1, 2: 2, 3: 3})
|
||||
m.Iterator(intIntCallBack)
|
||||
gtest.Assert(m.Map(), map[int]int{1: 1, 2: 2, 3: 3})
|
||||
m.BatchRemove([]int{1, 2})
|
||||
m.Removes([]int{1, 2})
|
||||
gtest.Assert(m.Map(), map[int]int{3: 3})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Iterator(t *testing.T){
|
||||
expect := map[int]int{1: 1, 2: 2}
|
||||
m := gmap.NewIntIntMapFrom(expect)
|
||||
m.Iterator(func(k int, v int) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k int, v int) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k int, v int) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, 2)
|
||||
gtest.Assert(j, 1)
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Lock(t *testing.T){
|
||||
expect := map[int]int{1: 1, 2: 2}
|
||||
m := gmap.NewIntIntMapFrom(expect)
|
||||
m.LockFunc(func(m map[int]int) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[int]int) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
|
||||
}
|
||||
func Test_IntIntMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
|
||||
|
||||
@ -1,91 +0,0 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getInterface() interface{} {
|
||||
return 123
|
||||
}
|
||||
func intInterfaceCallBack(int, interface{}) bool {
|
||||
return true
|
||||
}
|
||||
func Test_IntInterfaceMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntInterfaceMap()
|
||||
m.Set(1, 1)
|
||||
|
||||
gtest.Assert(m.Get(1), 1)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet(2, "2"), "2")
|
||||
gtest.Assert(m.SetIfNotExist(2, "2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
gtest.Assert(m.Remove(2), "2")
|
||||
gtest.Assert(m.Contains(2), false)
|
||||
|
||||
gtest.AssertIN(3, m.Keys())
|
||||
gtest.AssertIN(1, m.Keys())
|
||||
gtest.AssertIN(3, m.Values())
|
||||
gtest.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
gtest.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntInterfaceMapFrom(map[int]interface{}{1: 1, 2: "2"})
|
||||
gtest.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
m3 := gmap.NewIntInterfaceMapFromArray([]int{1, 2}, []interface{}{1, "2"})
|
||||
gtest.Assert(m3.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_IntInterfaceMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewIntInterfaceMap()
|
||||
|
||||
m.GetOrSetFunc(1, getInterface)
|
||||
m.GetOrSetFuncLock(2, getInterface)
|
||||
gtest.Assert(m.Get(1), 123)
|
||||
gtest.Assert(m.Get(2), 123)
|
||||
gtest.Assert(m.SetIfNotExistFunc(1, getInterface), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(2, getInterface), false)
|
||||
}
|
||||
|
||||
func Test_IntInterfaceMap_Batch(t *testing.T) {
|
||||
m := gmap.NewIntInterfaceMap()
|
||||
|
||||
m.BatchSet(map[int]interface{}{1: 1, 2: "2", 3: 3})
|
||||
m.Iterator(intInterfaceCallBack)
|
||||
gtest.Assert(m.Map(), map[int]interface{}{1: 1, 2: "2", 3: 3})
|
||||
m.BatchRemove([]int{1, 2})
|
||||
gtest.Assert(m.Map(), map[int]interface{}{3: 3})
|
||||
}
|
||||
|
||||
func Test_IntInterfaceMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntInterfaceMapFrom(map[int]interface{}{1: 1, 2: "2"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN(2, m.Keys())
|
||||
}
|
||||
func Test_IntInterfaceMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewIntInterfaceMap()
|
||||
m2 := gmap.NewIntInterfaceMap()
|
||||
m1.Set(1, 1)
|
||||
m2.Set(2, "2")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
}
|
||||
135
g/container/gmap/gmap_z_int_str_test.go
Normal file
135
g/container/gmap/gmap_z_int_str_test.go
Normal file
@ -0,0 +1,135 @@
|
||||
// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getStr() string {
|
||||
return "z"
|
||||
}
|
||||
func intStrCallBack(int, string) bool {
|
||||
return true
|
||||
}
|
||||
func Test_IntStrMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.Set(1, "a")
|
||||
|
||||
gtest.Assert(m.Get(1), "a")
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet(2, "b"), "b")
|
||||
gtest.Assert(m.SetIfNotExist(2, "b"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist(3, "c"), true)
|
||||
|
||||
gtest.Assert(m.Remove(2), "b")
|
||||
gtest.Assert(m.Contains(2), false)
|
||||
|
||||
gtest.AssertIN(3, m.Keys())
|
||||
gtest.AssertIN(1, m.Keys())
|
||||
gtest.AssertIN("a", m.Values())
|
||||
gtest.AssertIN("c", m.Values())
|
||||
|
||||
//反转之后不成为以下 map,flip 操作只是翻转原 map
|
||||
//gtest.Assert(m.Map(), map[string]int{"a": 1, "c": 3})
|
||||
m_f := gmap.NewIntStrMap()
|
||||
m_f.Set(1, "2")
|
||||
m_f.Flip()
|
||||
gtest.Assert(m_f.Map(), map[int]string{2: "1"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b"})
|
||||
gtest.Assert(m2.Map(), map[int]string{1: "a", 2: "b"})
|
||||
})
|
||||
}
|
||||
func Test_IntStrMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
|
||||
m.GetOrSetFunc(1, getStr)
|
||||
m.GetOrSetFuncLock(2, getStr)
|
||||
gtest.Assert(m.Get(1), "z")
|
||||
gtest.Assert(m.Get(2), "z")
|
||||
gtest.Assert(m.SetIfNotExistFunc(1, getStr), false)
|
||||
gtest.Assert(m.SetIfNotExistFunc(3, getStr), true)
|
||||
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(2, getStr), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(4, getStr), true)
|
||||
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Batch(t *testing.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
|
||||
m.Sets(map[int]string{1: "a", 2: "b", 3: "c"})
|
||||
gtest.Assert(m.Map(), map[int]string{1: "a", 2: "b",3: "c"})
|
||||
m.Removes([]int{1, 2})
|
||||
gtest.Assert(m.Map(), map[int]interface{}{3: "c"})
|
||||
}
|
||||
func Test_IntStrMap_Iterator(t *testing.T){
|
||||
expect := map[int]string{1: "a", 2: "b"}
|
||||
m := gmap.NewIntStrMapFrom(expect)
|
||||
m.Iterator(func(k int, v string) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k int, v string) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k int, v string) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, 2)
|
||||
gtest.Assert(j, 1)
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Lock(t *testing.T){
|
||||
|
||||
expect := map[int]string{1: "a", 2: "b", 3: "c"}
|
||||
m := gmap.NewIntStrMapFrom(expect)
|
||||
m.LockFunc(func(m map[int]string) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[int]string) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
|
||||
}
|
||||
func Test_IntStrMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b", 3: "c"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN(2, m.Keys())
|
||||
}
|
||||
func Test_IntStrMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewIntStrMap()
|
||||
m2 := gmap.NewIntStrMap()
|
||||
m1.Set(1, "a")
|
||||
m2.Set(2, "b")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[int]string{1: "a", 2: "b"})
|
||||
}
|
||||
@ -1,96 +0,0 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getString() string {
|
||||
return "z"
|
||||
}
|
||||
func intStringCallBack(int, string) bool {
|
||||
return true
|
||||
}
|
||||
func Test_IntStringMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntStringMap()
|
||||
m.Set(1, "a")
|
||||
|
||||
gtest.Assert(m.Get(1), "a")
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet(2, "b"), "b")
|
||||
gtest.Assert(m.SetIfNotExist(2, "b"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist(3, "c"), true)
|
||||
|
||||
gtest.Assert(m.Remove(2), "b")
|
||||
gtest.Assert(m.Contains(2), false)
|
||||
|
||||
gtest.AssertIN(3, m.Keys())
|
||||
gtest.AssertIN(1, m.Keys())
|
||||
gtest.AssertIN("a", m.Values())
|
||||
gtest.AssertIN("c", m.Values())
|
||||
|
||||
//反转之后不成为以下 map,flip 操作只是翻转原 map
|
||||
//gtest.Assert(m.Map(), map[string]int{"a": 1, "c": 3})
|
||||
m_f := gmap.NewIntStringMap()
|
||||
m_f.Set(1, "2")
|
||||
m_f.Flip()
|
||||
gtest.Assert(m_f.Map(), map[int]string{2: "1"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntStringMapFrom(map[int]string{1: "a", 2: "b"})
|
||||
gtest.Assert(m2.Map(), map[int]string{1: "a", 2: "b"})
|
||||
m3 := gmap.NewIntStringMapFromArray([]int{1, 2}, []string{"a", "b"})
|
||||
gtest.Assert(m3.Map(), map[int]string{1: "a", 2: "b"})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_IntStringMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewIntStringMap()
|
||||
|
||||
m.GetOrSetFunc(1, getString)
|
||||
m.GetOrSetFuncLock(2, getString)
|
||||
gtest.Assert(m.Get(1), "z")
|
||||
gtest.Assert(m.Get(2), "z")
|
||||
gtest.Assert(m.SetIfNotExistFunc(1, getString), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(2, getString), false)
|
||||
}
|
||||
|
||||
func Test_IntStringMap_Batch(t *testing.T) {
|
||||
m := gmap.NewIntStringMap()
|
||||
|
||||
m.BatchSet(map[int]string{1: "a", 2: "b", 3: "c"})
|
||||
m.Iterator(intStringCallBack)
|
||||
gtest.Assert(m.Map(), map[int]string{1: "a", 2: "b",3: "c"})
|
||||
m.BatchRemove([]int{1, 2})
|
||||
gtest.Assert(m.Map(), map[int]interface{}{3: "c"})
|
||||
}
|
||||
|
||||
func Test_IntStringMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntStringMapFrom(map[int]string{1: "a", 2: "b", 3: "c"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN(2, m.Keys())
|
||||
}
|
||||
func Test_IntStringMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewIntStringMap()
|
||||
m2 := gmap.NewIntStringMap()
|
||||
m1.Set(1, "a")
|
||||
m2.Set(2, "b")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[int]string{1: "a", 2: "b"})
|
||||
}
|
||||
120
g/container/gmap/gmap_z_link_map_test.go
Normal file
120
g/container/gmap/gmap_z_link_map_test.go
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_List_Map_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewListMap()
|
||||
m.Set("key1", "val1")
|
||||
gtest.Assert(m.Keys(), []interface{}{"key1"})
|
||||
|
||||
gtest.Assert(m.Get("key1"), "val1")
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("key2", "val2"), "val2")
|
||||
gtest.Assert(m.SetIfNotExist("key2", "val2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("key3", "val3"), true)
|
||||
gtest.Assert(m.Remove("key2"), "val2")
|
||||
gtest.Assert(m.Contains("key2"), false)
|
||||
|
||||
gtest.AssertIN("key3", m.Keys())
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
gtest.AssertIN("val3", m.Values())
|
||||
gtest.AssertIN("val1", m.Values())
|
||||
|
||||
m.Flip()
|
||||
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewListMapFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
})
|
||||
}
|
||||
func Test_List_Map_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewListMap()
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
m.GetOrSetFuncLock("funlock", getValue)
|
||||
gtest.Assert(m.Get("funlock"), 3)
|
||||
gtest.Assert(m.Get("fun"), 3)
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
|
||||
}
|
||||
|
||||
func Test_List_Map_Batch(t *testing.T) {
|
||||
m := gmap.NewListMap()
|
||||
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
m.Removes([]interface{}{"key1", 1})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
|
||||
}
|
||||
func Test_List_Map_Iterator(t *testing.T){
|
||||
expect :=map[interface{}]interface{}{1: 1, "key1": "val1"}
|
||||
|
||||
m := gmap.NewListMapFrom(expect)
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, 2)
|
||||
gtest.Assert(j, 1)
|
||||
}
|
||||
|
||||
func Test_List_Map_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewListMapFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove("key1")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
}
|
||||
|
||||
func Test_List_Map_Basic_Merge(t *testing.T) {
|
||||
m1 := gmap.NewListMap()
|
||||
m2 := gmap.NewListMap()
|
||||
m1.Set("key1", "val1")
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"})
|
||||
}
|
||||
|
||||
func Test_List_Map_Order(t *testing.T) {
|
||||
m := gmap.NewListMap()
|
||||
m.Set("k1", "v1")
|
||||
m.Set("k2", "v2")
|
||||
m.Set("k3", "v3")
|
||||
gtest.Assert(m.Keys(), g.Slice{"k1", "k2", "k3"})
|
||||
gtest.Assert(m.Values(), g.Slice{"v1", "v2", "v3"})
|
||||
}
|
||||
128
g/container/gmap/gmap_z_str_any_test.go
Normal file
128
g/container/gmap/gmap_z_str_any_test.go
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func stringAnyCallBack(string, interface{}) bool {
|
||||
return true
|
||||
}
|
||||
func Test_StrAnyMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStrAnyMap()
|
||||
m.Set("a", 1)
|
||||
|
||||
gtest.Assert(m.Get("a"), 1)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("b", "2"), "2")
|
||||
gtest.Assert(m.SetIfNotExist("b", "2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("c", 3), true)
|
||||
|
||||
gtest.Assert(m.Remove("b"), "2")
|
||||
gtest.Assert(m.Contains("b"), false)
|
||||
|
||||
gtest.AssertIN("c", m.Keys())
|
||||
gtest.AssertIN("a", m.Keys())
|
||||
gtest.AssertIN(3, m.Values())
|
||||
gtest.AssertIN(1, m.Values())
|
||||
|
||||
m.Flip()
|
||||
gtest.Assert(m.Map(), map[string]interface{}{"1": "a", "3": "c"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewStrAnyMapFrom(map[string]interface{}{"a": 1, "b": "2"})
|
||||
gtest.Assert(m2.Map(), map[string]interface{}{"a": 1, "b": "2"})
|
||||
})
|
||||
}
|
||||
func Test_StrAnyMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewStrAnyMap()
|
||||
|
||||
m.GetOrSetFunc("a", getAny)
|
||||
m.GetOrSetFuncLock("b", getAny)
|
||||
gtest.Assert(m.Get("a"), 123)
|
||||
gtest.Assert(m.Get("b"), 123)
|
||||
gtest.Assert(m.SetIfNotExistFunc("a", getAny), false)
|
||||
gtest.Assert(m.SetIfNotExistFunc("c", getAny), true)
|
||||
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("b", getAny), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("d", getAny), true)
|
||||
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Batch(t *testing.T) {
|
||||
m := gmap.NewStrAnyMap()
|
||||
|
||||
m.Sets(map[string]interface{}{"a": 1, "b": "2", "c": 3})
|
||||
gtest.Assert(m.Map(), map[string]interface{}{"a": 1, "b": "2", "c": 3})
|
||||
m.Removes([]string{"a", "b"})
|
||||
gtest.Assert(m.Map(), map[string]interface{}{"c": 3})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Iterator(t *testing.T) {
|
||||
expect := map[string]interface{}{"a": true, "b": false}
|
||||
m := gmap.NewStrAnyMapFrom(expect)
|
||||
m.Iterator(func(k string, v interface{}) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k string, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k string, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, 2)
|
||||
gtest.Assert(j, 1)
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Lock(t *testing.T) {
|
||||
expect := map[string]interface{}{"a": true, "b": false}
|
||||
|
||||
m := gmap.NewStrAnyMapFrom(expect)
|
||||
m.LockFunc(func(m map[string]interface{}) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[string]interface{}) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
}
|
||||
func Test_StrAnyMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewStrAnyMapFrom(map[string]interface{}{"a": 1, "b": "2"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove("a")
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN("a", m_clone.Keys())
|
||||
|
||||
m_clone.Remove("b")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("b", m.Keys())
|
||||
}
|
||||
func Test_StrAnyMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewStrAnyMap()
|
||||
m2 := gmap.NewStrAnyMap()
|
||||
m1.Set("a", 1)
|
||||
m2.Set("b", "2")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[string]interface{}{"a": 1, "b": "2"})
|
||||
}
|
||||
131
g/container/gmap/gmap_z_str_int_test.go
Normal file
131
g/container/gmap/gmap_z_str_int_test.go
Normal file
@ -0,0 +1,131 @@
|
||||
// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func stringIntCallBack(string, int) bool {
|
||||
return true
|
||||
}
|
||||
func Test_StrIntMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStrIntMap()
|
||||
m.Set("a", 1)
|
||||
|
||||
gtest.Assert(m.Get("a"), 1)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("b", 2), 2)
|
||||
gtest.Assert(m.SetIfNotExist("b", 2), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("c", 3), true)
|
||||
|
||||
gtest.Assert(m.Remove("b"), 2)
|
||||
gtest.Assert(m.Contains("b"), false)
|
||||
|
||||
gtest.AssertIN("c", m.Keys())
|
||||
gtest.AssertIN("a", m.Keys())
|
||||
gtest.AssertIN(3, m.Values())
|
||||
gtest.AssertIN(1, m.Values())
|
||||
|
||||
m_f := gmap.NewStrIntMap()
|
||||
m_f.Set("1", 2)
|
||||
m_f.Flip()
|
||||
gtest.Assert(m_f.Map(), map[string]int{"2": 1})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewStrIntMapFrom(map[string]int{"a": 1, "b": 2})
|
||||
gtest.Assert(m2.Map(), map[string]int{"a": 1, "b": 2})
|
||||
})
|
||||
}
|
||||
func Test_StrIntMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewStrIntMap()
|
||||
|
||||
m.GetOrSetFunc("a", getInt)
|
||||
m.GetOrSetFuncLock("b", getInt)
|
||||
gtest.Assert(m.Get("a"), 123)
|
||||
gtest.Assert(m.Get("b"), 123)
|
||||
gtest.Assert(m.SetIfNotExistFunc("a", getInt), false)
|
||||
gtest.Assert(m.SetIfNotExistFunc("c", getInt), true)
|
||||
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("b", getInt), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("d", getInt), true)
|
||||
|
||||
}
|
||||
|
||||
func Test_StrIntMap_Batch(t *testing.T) {
|
||||
m := gmap.NewStrIntMap()
|
||||
|
||||
m.Sets(map[string]int{"a": 1, "b": 2, "c": 3})
|
||||
gtest.Assert(m.Map(), map[string]int{"a": 1, "b": 2, "c": 3})
|
||||
m.Removes([]string{"a", "b"})
|
||||
gtest.Assert(m.Map(), map[string]int{"c": 3})
|
||||
}
|
||||
func Test_StrIntMap_Iterator(t *testing.T) {
|
||||
expect := map[string]int{"a": 1, "b": 2}
|
||||
m := gmap.NewStrIntMapFrom(expect)
|
||||
m.Iterator(func(k string, v int) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k string, v int) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k string, v int) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, 2)
|
||||
gtest.Assert(j, 1)
|
||||
|
||||
}
|
||||
|
||||
func Test_StrIntMap_Lock(t *testing.T) {
|
||||
expect := map[string]int{"a": 1, "b": 2}
|
||||
|
||||
m := gmap.NewStrIntMapFrom(expect)
|
||||
m.LockFunc(func(m map[string]int) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[string]int) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrIntMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewStrIntMapFrom(map[string]int{"a": 1, "b": 2, "c": 3})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove("a")
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN("a", m_clone.Keys())
|
||||
|
||||
m_clone.Remove("b")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("b", m.Keys())
|
||||
}
|
||||
func Test_StrIntMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewStrIntMap()
|
||||
m2 := gmap.NewStrIntMap()
|
||||
m1.Set("a", 1)
|
||||
m2.Set("b", 2)
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[string]int{"a": 1, "b": 2})
|
||||
}
|
||||
128
g/container/gmap/gmap_z_str_str_test.go
Normal file
128
g/container/gmap/gmap_z_str_str_test.go
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func stringStrCallBack(string, string) bool {
|
||||
return true
|
||||
}
|
||||
func Test_StrStrMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStrStrMap()
|
||||
m.Set("a", "a")
|
||||
|
||||
gtest.Assert(m.Get("a"), "a")
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("b", "b"), "b")
|
||||
gtest.Assert(m.SetIfNotExist("b", "b"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("c", "c"), true)
|
||||
|
||||
gtest.Assert(m.Remove("b"), "b")
|
||||
gtest.Assert(m.Contains("b"), false)
|
||||
|
||||
gtest.AssertIN("c", m.Keys())
|
||||
gtest.AssertIN("a", m.Keys())
|
||||
gtest.AssertIN("a", m.Values())
|
||||
gtest.AssertIN("c", m.Values())
|
||||
|
||||
m.Flip()
|
||||
|
||||
gtest.Assert(m.Map(), map[string]string{"a": "a", "c": "c"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewStrStrMapFrom(map[string]string{"a": "a", "b": "b"})
|
||||
gtest.Assert(m2.Map(), map[string]string{"a": "a", "b": "b"})
|
||||
})
|
||||
}
|
||||
func Test_StrStrMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewStrStrMap()
|
||||
|
||||
m.GetOrSetFunc("a", getStr)
|
||||
m.GetOrSetFuncLock("b", getStr)
|
||||
gtest.Assert(m.Get("a"), "z")
|
||||
gtest.Assert(m.Get("b"), "z")
|
||||
gtest.Assert(m.SetIfNotExistFunc("a", getStr), false)
|
||||
gtest.Assert(m.SetIfNotExistFunc("c", getStr), true)
|
||||
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("b", getStr), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("d", getStr), true)
|
||||
|
||||
}
|
||||
|
||||
func Test_StrStrMap_Batch(t *testing.T) {
|
||||
m := gmap.NewStrStrMap()
|
||||
|
||||
m.Sets(map[string]string{"a": "a", "b": "b", "c": "c"})
|
||||
gtest.Assert(m.Map(), map[string]string{"a": "a", "b": "b", "c": "c"})
|
||||
m.Removes([]string{"a", "b"})
|
||||
gtest.Assert(m.Map(), map[string]string{"c": "c"})
|
||||
}
|
||||
func Test_StrStrMap_Iterator(t *testing.T) {
|
||||
expect := map[string]string{"a": "a", "b": "b"}
|
||||
m := gmap.NewStrStrMapFrom(expect)
|
||||
m.Iterator(func(k string, v string) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k string, v string) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k string, v string) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, 2)
|
||||
gtest.Assert(j, 1)
|
||||
}
|
||||
|
||||
func Test_StrStrMap_Lock(t *testing.T) {
|
||||
expect := map[string]string{"a": "a", "b": "b"}
|
||||
|
||||
m := gmap.NewStrStrMapFrom(expect)
|
||||
m.LockFunc(func(m map[string]string) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[string]string) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
}
|
||||
func Test_StrStrMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewStrStrMapFrom(map[string]string{"a": "a", "b": "b", "c": "c"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove("a")
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN("a", m_clone.Keys())
|
||||
|
||||
m_clone.Remove("b")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("b", m.Keys())
|
||||
}
|
||||
func Test_StrStrMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewStrStrMap()
|
||||
m2 := gmap.NewStrStrMap()
|
||||
m1.Set("a", "a")
|
||||
m2.Set("b", "b")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[string]string{"a": "a", "b": "b"})
|
||||
}
|
||||
@ -1,85 +0,0 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func StringBoolCallBack( string, bool) bool {
|
||||
return true
|
||||
}
|
||||
func Test_StringBoolMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStringBoolMap()
|
||||
m.Set("a", true)
|
||||
|
||||
gtest.Assert(m.Get("a"), true)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("b", false), false)
|
||||
gtest.Assert(m.SetIfNotExist("b", false), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("c", false), true)
|
||||
|
||||
gtest.Assert(m.Remove("b"), false)
|
||||
gtest.Assert(m.Contains("b"), false)
|
||||
|
||||
gtest.AssertIN("c", m.Keys())
|
||||
gtest.AssertIN("a", m.Keys())
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewStringBoolMapFrom(map[string]bool{"a": true, "b": false})
|
||||
gtest.Assert(m2.Map(), map[string]bool{"a": true, "b": false})
|
||||
m3 := gmap.NewStringBoolMapFromArray([]string{"a", "b"}, []bool{true, false})
|
||||
gtest.Assert(m3.Map(), map[string]bool{"a": true, "b": false})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_StringBoolMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewStringBoolMap()
|
||||
|
||||
m.GetOrSetFunc("a", getBool)
|
||||
m.GetOrSetFuncLock("b", getBool)
|
||||
gtest.Assert(m.Get("a"), true)
|
||||
gtest.Assert(m.Get("b"), true)
|
||||
gtest.Assert(m.SetIfNotExistFunc("a", getBool), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("b", getBool), false)
|
||||
}
|
||||
|
||||
func Test_StringBoolMap_Batch(t *testing.T) {
|
||||
m := gmap.NewStringBoolMap()
|
||||
|
||||
m.BatchSet(map[string]bool{"a": true, "b": false, "c": true})
|
||||
m.Iterator(StringBoolCallBack)
|
||||
gtest.Assert(m.Map(), map[string]bool{"a": true, "b": false, "c": true})
|
||||
m.BatchRemove([]string{"a", "b"})
|
||||
gtest.Assert(m.Map(), map[string]bool{"c": true})
|
||||
}
|
||||
|
||||
func Test_StringBoolMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewStringBoolMapFrom(map[string]bool{"a": true, "b": false})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove("a")
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN("a", m_clone.Keys())
|
||||
|
||||
m_clone.Remove("b")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("b", m.Keys())
|
||||
}
|
||||
func Test_StringBoolMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewStringBoolMap()
|
||||
m2 := gmap.NewStringBoolMap()
|
||||
m1.Set("a", true)
|
||||
m2.Set("b", false)
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[string]bool{"a": true, "b": false})
|
||||
}
|
||||
@ -1,91 +0,0 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func stringIntCallBack(string, int) bool {
|
||||
return true
|
||||
}
|
||||
func Test_StringIntMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStringIntMap()
|
||||
m.Set("a", 1)
|
||||
|
||||
gtest.Assert(m.Get("a"), 1)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("b", 2), 2)
|
||||
gtest.Assert(m.SetIfNotExist("b", 2), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("c", 3), true)
|
||||
|
||||
gtest.Assert(m.Remove("b"), 2)
|
||||
gtest.Assert(m.Contains("b"), false)
|
||||
|
||||
gtest.AssertIN("c", m.Keys())
|
||||
gtest.AssertIN("a", m.Keys())
|
||||
gtest.AssertIN(3, m.Values())
|
||||
gtest.AssertIN(1, m.Values())
|
||||
|
||||
m_f := gmap.NewStringIntMap()
|
||||
m_f.Set("1", 2)
|
||||
m_f.Flip()
|
||||
gtest.Assert(m_f.Map(), map[string]int{"2": 1})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewStringIntMapFrom(map[string]int{"a": 1, "b": 2})
|
||||
gtest.Assert(m2.Map(), map[string]int{"a": 1, "b": 2})
|
||||
m3 := gmap.NewStringIntMapFromArray([]string{"a", "b"}, []int{1, 2})
|
||||
gtest.Assert(m3.Map(), map[string]int{"a": 1, "b": 2})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_StringIntMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewStringIntMap()
|
||||
|
||||
m.GetOrSetFunc("a", getInt)
|
||||
m.GetOrSetFuncLock("b", getInt)
|
||||
gtest.Assert(m.Get("a"), 123)
|
||||
gtest.Assert(m.Get("b"), 123)
|
||||
gtest.Assert(m.SetIfNotExistFunc("a", getInt), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("b", getInt), false)
|
||||
}
|
||||
|
||||
func Test_StringIntMap_Batch(t *testing.T) {
|
||||
m := gmap.NewStringIntMap()
|
||||
|
||||
m.BatchSet(map[string]int{"a": 1, "b": 2, "c": 3})
|
||||
m.Iterator(stringIntCallBack)
|
||||
gtest.Assert(m.Map(), map[string]int{"a": 1, "b": 2, "c": 3})
|
||||
m.BatchRemove([]string{"a", "b"})
|
||||
gtest.Assert(m.Map(), map[string]int{"c": 3})
|
||||
}
|
||||
|
||||
func Test_StringIntMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewStringIntMapFrom(map[string]int{"a": 1, "b": 2, "c": 3})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove("a")
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN("a", m_clone.Keys())
|
||||
|
||||
m_clone.Remove("b")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("b", m.Keys())
|
||||
}
|
||||
func Test_StringIntMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewStringIntMap()
|
||||
m2 := gmap.NewStringIntMap()
|
||||
m1.Set("a", 1)
|
||||
m2.Set("b", 2)
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[string]int{"a": 1, "b": 2})
|
||||
}
|
||||
@ -1,89 +0,0 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func stringInterfaceCallBack(string, interface{}) bool {
|
||||
return true
|
||||
}
|
||||
func Test_StringInterfaceMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStringInterfaceMap()
|
||||
m.Set("a", 1)
|
||||
|
||||
gtest.Assert(m.Get("a"), 1)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("b", "2"), "2")
|
||||
gtest.Assert(m.SetIfNotExist("b", "2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("c", 3), true)
|
||||
|
||||
gtest.Assert(m.Remove("b"), "2")
|
||||
gtest.Assert(m.Contains("b"), false)
|
||||
|
||||
gtest.AssertIN("c", m.Keys())
|
||||
gtest.AssertIN("a", m.Keys())
|
||||
gtest.AssertIN(3, m.Values())
|
||||
gtest.AssertIN(1, m.Values())
|
||||
|
||||
m.Flip()
|
||||
gtest.Assert(m.Map(), map[string]interface{}{"1": "a", "3": "c"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewStringInterfaceMapFrom(map[string]interface{}{"a": 1, "b": "2"})
|
||||
gtest.Assert(m2.Map(), map[string]interface{}{"a": 1, "b": "2"})
|
||||
m3 := gmap.NewStringInterfaceMapFromArray([]string{"a", "b"}, []interface{}{1, "2"})
|
||||
gtest.Assert(m3.Map(), map[string]interface{}{"a": 1, "b": "2"})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_StringInterfaceMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewStringInterfaceMap()
|
||||
|
||||
m.GetOrSetFunc("a", getInterface)
|
||||
m.GetOrSetFuncLock("b", getInterface)
|
||||
gtest.Assert(m.Get("a"), 123)
|
||||
gtest.Assert(m.Get("b"), 123)
|
||||
gtest.Assert(m.SetIfNotExistFunc("a", getInterface), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("b", getInterface), false)
|
||||
}
|
||||
|
||||
func Test_StringInterfaceMap_Batch(t *testing.T) {
|
||||
m := gmap.NewStringInterfaceMap()
|
||||
|
||||
m.BatchSet(map[string]interface{}{"a": 1, "b": "2", "c": 3})
|
||||
m.Iterator(stringInterfaceCallBack)
|
||||
gtest.Assert(m.Map(), map[string]interface{}{"a": 1, "b": "2", "c": 3})
|
||||
m.BatchRemove([]string{"a", "b"})
|
||||
gtest.Assert(m.Map(), map[string]interface{}{"c": 3})
|
||||
}
|
||||
|
||||
func Test_StringInterfaceMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewStringInterfaceMapFrom(map[string]interface{}{"a": 1, "b": "2"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove("a")
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN("a", m_clone.Keys())
|
||||
|
||||
m_clone.Remove("b")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("b", m.Keys())
|
||||
}
|
||||
func Test_StringInterfaceMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewStringInterfaceMap()
|
||||
m2 := gmap.NewStringInterfaceMap()
|
||||
m1.Set("a", 1)
|
||||
m2.Set("b", "2")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[string]interface{}{"a": 1, "b": "2"})
|
||||
}
|
||||
@ -1,90 +0,0 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func stringStringCallBack(string, string) bool {
|
||||
return true
|
||||
}
|
||||
func Test_StringStringMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStringStringMap()
|
||||
m.Set("a", "a")
|
||||
|
||||
gtest.Assert(m.Get("a"), "a")
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("b", "b"), "b")
|
||||
gtest.Assert(m.SetIfNotExist("b", "b"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("c", "c"), true)
|
||||
|
||||
gtest.Assert(m.Remove("b"), "b")
|
||||
gtest.Assert(m.Contains("b"), false)
|
||||
|
||||
gtest.AssertIN("c", m.Keys())
|
||||
gtest.AssertIN("a", m.Keys())
|
||||
gtest.AssertIN("a", m.Values())
|
||||
gtest.AssertIN("c", m.Values())
|
||||
|
||||
m.Flip()
|
||||
|
||||
gtest.Assert(m.Map(), map[string]string{"a": "a", "c": "c"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewStringStringMapFrom(map[string]string{"a": "a", "b": "b"})
|
||||
gtest.Assert(m2.Map(), map[string]string{"a": "a", "b": "b"})
|
||||
m3 := gmap.NewStringStringMapFromArray([]string{"a", "b"}, []string{"a", "b"})
|
||||
gtest.Assert(m3.Map(), map[string]string{"a": "a", "b": "b"})
|
||||
|
||||
})
|
||||
}
|
||||
func Test_StringStringMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewStringStringMap()
|
||||
|
||||
m.GetOrSetFunc("a", getString)
|
||||
m.GetOrSetFuncLock("b", getString)
|
||||
gtest.Assert(m.Get("a"), "z")
|
||||
gtest.Assert(m.Get("b"), "z")
|
||||
gtest.Assert(m.SetIfNotExistFunc("a", getString), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("b", getString), false)
|
||||
}
|
||||
|
||||
func Test_StringStringMap_Batch(t *testing.T) {
|
||||
m := gmap.NewStringStringMap()
|
||||
|
||||
m.BatchSet(map[string]string{"a": "a", "b": "b", "c": "c"})
|
||||
m.Iterator(stringStringCallBack)
|
||||
gtest.Assert(m.Map(), map[string]string{"a": "a", "b": "b", "c": "c"})
|
||||
m.BatchRemove([]string{"a", "b"})
|
||||
gtest.Assert(m.Map(), map[string]string{"c": "c"})
|
||||
}
|
||||
|
||||
func Test_StringStringMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewStringStringMapFrom(map[string]string{"a": "a", "b": "b", "c": "c"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove("a")
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN("a", m_clone.Keys())
|
||||
|
||||
m_clone.Remove("b")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("b", m.Keys())
|
||||
}
|
||||
func Test_StringStringMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewStringStringMap()
|
||||
m2 := gmap.NewStringStringMap()
|
||||
m1.Set("a", "a")
|
||||
m2.Set("b", "b")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[string]string{"a": "a", "b": "b"})
|
||||
}
|
||||
103
g/container/gmap/gmap_z_tree_map_test.go
Normal file
103
g/container/gmap/gmap_z_tree_map_test.go
Normal file
@ -0,0 +1,103 @@
|
||||
// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"github.com/gogf/gf/g/util/gutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func Test_Tree_Map_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewTreeMap(gutil.ComparatorString)
|
||||
m.Set("key1", "val1")
|
||||
gtest.Assert(m.Keys(), []interface{}{"key1"})
|
||||
|
||||
gtest.Assert(m.Get("key1"), "val1")
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("key2", "val2"), "val2")
|
||||
gtest.Assert(m.SetIfNotExist("key2", "val2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("key3", "val3"), true)
|
||||
|
||||
gtest.Assert(m.Remove("key2"), "val2")
|
||||
gtest.Assert(m.Contains("key2"), false)
|
||||
|
||||
gtest.AssertIN("key3", m.Keys())
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
gtest.AssertIN("val3", m.Values())
|
||||
gtest.AssertIN("val1", m.Values())
|
||||
|
||||
m.Flip()
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewTreeMapFrom(gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
})
|
||||
}
|
||||
func Test_Tree_Map_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewTreeMap(gutil.ComparatorString)
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
m.GetOrSetFuncLock("funlock", getValue)
|
||||
gtest.Assert(m.Get("funlock"), 3)
|
||||
gtest.Assert(m.Get("fun"), 3)
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
|
||||
}
|
||||
|
||||
func Test_Tree_Map_Batch(t *testing.T) {
|
||||
m := gmap.NewTreeMap(gutil.ComparatorString)
|
||||
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
m.Removes([]interface{}{"key1", 1})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
|
||||
}
|
||||
func Test_Tree_Map_Iterator(t *testing.T){
|
||||
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
|
||||
|
||||
m := gmap.NewTreeMapFrom(gutil.ComparatorString, expect)
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, 2)
|
||||
gtest.Assert(j, 1)
|
||||
}
|
||||
|
||||
func Test_Tree_Map_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewTreeMapFrom(gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove("key1")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
}
|
||||
@ -4,7 +4,7 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gpool provides a object-reusable concurrent-safe pool.
|
||||
// Package gpool provides object-reusable concurrent-safe pool.
|
||||
package gpool
|
||||
|
||||
import (
|
||||
@ -16,31 +16,38 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// 对象池
|
||||
// Object-Reusable Pool.
|
||||
type Pool struct {
|
||||
list *glist.List // 可用/闲置的文件指针链表
|
||||
closed *gtype.Bool // 连接池是否已关闭
|
||||
Expire int64 // (毫秒)闲置最大时间,超过该时间则被系统回收
|
||||
NewFunc func()(interface{}, error) // 创建对象的方法定义
|
||||
ExpireFunc func(interface{}) // 对象的过期销毁方法(当池对象销毁需要执行额外的销毁操作时,需要定义该方法)
|
||||
// 例如: net.Conn, os.File等对象都需要执行额外关闭操作
|
||||
list *glist.List // Available/idle list.
|
||||
closed *gtype.Bool // Whether the pool is closed.
|
||||
Expire int64 // Max idle time(ms), after which it is recycled.
|
||||
NewFunc func()(interface{}, error) // Callback function to create item.
|
||||
ExpireFunc func(interface{}) // Expired destruction function for objects.
|
||||
// This function needs to be defined when the pool object
|
||||
// needs to perform additional destruction operations.
|
||||
// Eg: net.Conn, os.File, etc.
|
||||
}
|
||||
|
||||
// 对象池数据项
|
||||
// Pool item.
|
||||
type poolItem struct {
|
||||
expire int64 // (毫秒)过期时间
|
||||
value interface{} // 对象值
|
||||
expire int64 // Expire time(millisecond).
|
||||
value interface{} // Value.
|
||||
}
|
||||
|
||||
// 对象创建方法类型
|
||||
// Creation function for object.
|
||||
type NewFunc func() (interface{}, error)
|
||||
|
||||
// 对象过期方法类型
|
||||
// Destruction function for object.
|
||||
type ExpireFunc func(interface{})
|
||||
|
||||
// 创建一个对象池,为保证执行效率,过期时间一旦设定之后无法修改
|
||||
// expire = 0表示不过期,expire < 0表示使用完立即回收,expire > 0表示超时回收
|
||||
// 注意过期时间单位为**毫秒**
|
||||
|
||||
// New returns a new object pool.
|
||||
// To ensure execution efficiency, the expiration time cannot be modified once it is set.
|
||||
// Expire:
|
||||
// expire = 0 : not expired;
|
||||
// expire < 0 : immediate recovery after use;
|
||||
// expire > 0 : timeout recovery;
|
||||
// Note that the expiration time unit is ** milliseconds **.
|
||||
func New(expire int, newFunc NewFunc, expireFunc...ExpireFunc) *Pool {
|
||||
r := &Pool {
|
||||
list : glist.New(),
|
||||
@ -55,7 +62,7 @@ func New(expire int, newFunc NewFunc, expireFunc...ExpireFunc) *Pool {
|
||||
return r
|
||||
}
|
||||
|
||||
// 放一个临时对象到池中
|
||||
// Put puts an item to pool.
|
||||
func (p *Pool) Put(value interface{}) {
|
||||
item := &poolItem {
|
||||
value : value,
|
||||
@ -68,12 +75,12 @@ func (p *Pool) Put(value interface{}) {
|
||||
p.list.PushBack(item)
|
||||
}
|
||||
|
||||
// 清空对象池
|
||||
// Clear clears pool, which means it will remove all items from pool.
|
||||
func (p *Pool) Clear() {
|
||||
p.list.RemoveAll()
|
||||
}
|
||||
|
||||
// 从池中获得一个临时对象
|
||||
// Get picks an item from pool.
|
||||
func (p *Pool) Get() (interface{}, error) {
|
||||
for !p.closed.Val() {
|
||||
if r := p.list.PopFront(); r != nil {
|
||||
@ -91,17 +98,17 @@ func (p *Pool) Get() (interface{}, error) {
|
||||
return nil, errors.New("pool is empty")
|
||||
}
|
||||
|
||||
// 查询当前池中的对象数量
|
||||
// Size returns the count of available items of pool.
|
||||
func (p *Pool) Size() int {
|
||||
return p.list.Len()
|
||||
}
|
||||
|
||||
// 关闭池
|
||||
// Close closes the pool.
|
||||
func (p *Pool) Close() {
|
||||
p.closed.Set(true)
|
||||
}
|
||||
|
||||
// 超时检测循环
|
||||
// checkExpire secondly removes expired items from pool.
|
||||
func (p *Pool) checkExpire() {
|
||||
if p.closed.Val() {
|
||||
gtimer.Exit()
|
||||
|
||||
@ -4,14 +4,18 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gqueue provides a dynamic/static concurrent-safe(alternative) queue.
|
||||
// Package gqueue provides a dynamic/static concurrent-safe queue.
|
||||
//
|
||||
// 并发安全动态队列.
|
||||
// Features:
|
||||
//
|
||||
// 1. FIFO queue(data -> list -> chan);
|
||||
//
|
||||
// 2. Fast creation and initialization;
|
||||
//
|
||||
// 3. Support dynamic queue size(unlimited queue size);
|
||||
//
|
||||
// 4. Blocking when reading data from queue;
|
||||
//
|
||||
// 特点:
|
||||
// 1. 动态队列初始化速度快;
|
||||
// 2. 动态的队列大小(不限大小);
|
||||
// 3. 取数据时如果队列为空那么会阻塞等待;
|
||||
package gqueue
|
||||
|
||||
import (
|
||||
@ -19,27 +23,22 @@ import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// 1、这是一个先进先出的队列(chan <-- list);
|
||||
//
|
||||
// 2、当创建Queue对象时限定大小,那么等同于一个同步的chan并发安全队列;
|
||||
//
|
||||
// 3、不限制大小时,list链表用以存储数据,临时chan负责为客户端读取数据,当从chan获取数据时,list往chan中不停补充数据;
|
||||
//
|
||||
// 4、由于功能主体是chan,那么操作仍然像chan那样具有阻塞效果;
|
||||
type Queue struct {
|
||||
limit int // 队列限制大小
|
||||
list *glist.List // 底层数据链表
|
||||
events chan struct{} // 写入事件通知
|
||||
closed chan struct{} // 队列关闭通知
|
||||
C chan interface{} // 队列数据读取
|
||||
limit int // Limit for queue size.
|
||||
list *glist.List // Underlying list structure for data maintaining.
|
||||
events chan struct{} // Events for data writing.
|
||||
closed chan struct{} // Events for queue closing.
|
||||
C chan interface{} // Underlying channel for data reading.
|
||||
}
|
||||
|
||||
const (
|
||||
// 动态队列缓冲区大小
|
||||
// Size for queue buffer.
|
||||
gDEFAULT_QUEUE_SIZE = 10000
|
||||
)
|
||||
|
||||
// 队列大小为非必须参数,默认不限制
|
||||
// New returns an empty queue object.
|
||||
// Optional parameter <limit> is used to limit the size of the queue, which is unlimited by default.
|
||||
// When <limit> is given, the queue will be static and high performance which is comparable with stdlib chan.
|
||||
func New(limit...int) *Queue {
|
||||
q := &Queue {
|
||||
closed : make(chan struct{}, 0),
|
||||
@ -56,7 +55,8 @@ func New(limit...int) *Queue {
|
||||
return q
|
||||
}
|
||||
|
||||
// 异步list->chan同步队列
|
||||
// startAsyncLoop starts an asynchronous goroutine,
|
||||
// which handles the data synchronization from list <q.list> to channel <q.C>.
|
||||
func (q *Queue) startAsyncLoop() {
|
||||
for {
|
||||
select {
|
||||
@ -84,7 +84,8 @@ func (q *Queue) startAsyncLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
// 将数据压入队列, 队尾
|
||||
// Push pushes the data <v> into the queue.
|
||||
// Note that it would panics if Push is called after the queue is closed.
|
||||
func (q *Queue) Push(v interface{}) {
|
||||
if q.limit > 0 {
|
||||
q.C <- v
|
||||
@ -94,19 +95,22 @@ func (q *Queue) Push(v interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// 从队头先进先出地从队列取出一项数据
|
||||
// Pop pops an item from the queue in FIFO way.
|
||||
// Note that it would return nil immediately if Pop is called after the queue is closed.
|
||||
func (q *Queue) Pop() interface{} {
|
||||
return <- q.C
|
||||
}
|
||||
|
||||
// 关闭队列(通知所有通过Pop*阻塞的协程退出)
|
||||
// Close closes the queue.
|
||||
// Notice: It would notify all goroutines return immediately,
|
||||
// which are being blocked reading by Pop method.
|
||||
func (q *Queue) Close() {
|
||||
close(q.C)
|
||||
close(q.events)
|
||||
close(q.closed)
|
||||
}
|
||||
|
||||
// 获取当前队列大小
|
||||
// Size returns the length of the queue.
|
||||
func (q *Queue) Size() int {
|
||||
return len(q.C) + q.list.Len()
|
||||
}
|
||||
|
||||
@ -14,11 +14,12 @@ import (
|
||||
)
|
||||
|
||||
type Ring struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
ring *ring.Ring // 底层环形数据结构
|
||||
len *gtype.Int // 数据大小(已使用的大小)
|
||||
cap *gtype.Int // 总长度(分配的环大小,包括未使用的数据项数量)
|
||||
dirty *gtype.Bool // 标记环是否脏了(需要重新计算大小,当环大小发生改变时做标记)
|
||||
mu *rwmutex.RWMutex
|
||||
ring *ring.Ring // Underlying ring.
|
||||
len *gtype.Int // Length(already used size).
|
||||
cap *gtype.Int // Capability(>=len).
|
||||
dirty *gtype.Bool // Dirty, which means the len and cap should be recalculated.
|
||||
// It's marked dirty when the size of ring changes.
|
||||
}
|
||||
|
||||
func New(cap int, unsafe...bool) *Ring {
|
||||
@ -31,7 +32,7 @@ func New(cap int, unsafe...bool) *Ring {
|
||||
}
|
||||
}
|
||||
|
||||
// 返回当前环指向的数据项值
|
||||
// Val returns the item's value of current position.
|
||||
func (r *Ring) Val() interface{} {
|
||||
r.mu.RLock()
|
||||
v := r.ring.Value
|
||||
@ -39,19 +40,19 @@ func (r *Ring) Val() interface{} {
|
||||
return v
|
||||
}
|
||||
|
||||
// 返回当前环已有数据项大小
|
||||
// Len returns the size of ring.
|
||||
func (r *Ring) Len() int {
|
||||
r.checkAndUpdateLenAndCap()
|
||||
return r.len.Val()
|
||||
}
|
||||
|
||||
// 返回当前环总大小(包含未使用长度)
|
||||
// Cap returns the capacity of ring.
|
||||
func (r *Ring) Cap() int {
|
||||
r.checkAndUpdateLenAndCap()
|
||||
return r.cap.Val()
|
||||
}
|
||||
|
||||
// 检测并执行len和cap的更新(两者必须一起更新)
|
||||
// Checks and updates the len and cap of ring when ring is dirty.
|
||||
func (r *Ring) checkAndUpdateLenAndCap() {
|
||||
if !r.dirty.Val() {
|
||||
return
|
||||
@ -73,7 +74,7 @@ func (r *Ring) checkAndUpdateLenAndCap() {
|
||||
r.dirty.Set(false)
|
||||
}
|
||||
|
||||
// 当前位置设置数据项值
|
||||
// Set sets value to the item of current position.
|
||||
func (r *Ring) Set(value interface{}) *Ring {
|
||||
r.mu.Lock()
|
||||
if r.ring.Value == nil {
|
||||
@ -84,7 +85,7 @@ func (r *Ring) Set(value interface{}) *Ring {
|
||||
return r
|
||||
}
|
||||
|
||||
// Set & Next
|
||||
// Put sets <value> to current item of ring and moves position to next item.
|
||||
func (r *Ring) Put(value interface{}) *Ring {
|
||||
r.mu.Lock()
|
||||
if r.ring.Value == nil {
|
||||
@ -96,7 +97,8 @@ func (r *Ring) Put(value interface{}) *Ring {
|
||||
return r
|
||||
}
|
||||
|
||||
// 环往后(n > 0)或者往前(n < 0)移动n个元素
|
||||
// Move moves n % r.Len() elements backward (n < 0) or forward (n >= 0)
|
||||
// in the ring and returns that ring element. r must not be empty.
|
||||
func (r *Ring) Move(n int) *Ring {
|
||||
r.mu.Lock()
|
||||
r.ring = r.ring.Move(n)
|
||||
@ -104,7 +106,7 @@ func (r *Ring) Move(n int) *Ring {
|
||||
return r
|
||||
}
|
||||
|
||||
// 环往前移动1个元素
|
||||
// Prev returns the previous ring element. r must not be empty.
|
||||
func (r *Ring) Prev() *Ring {
|
||||
r.mu.Lock()
|
||||
r.ring = r.ring.Prev()
|
||||
@ -112,7 +114,7 @@ func (r *Ring) Prev() *Ring {
|
||||
return r
|
||||
}
|
||||
|
||||
// 环往后移动1个元素
|
||||
// Next returns the next ring element. r must not be empty.
|
||||
func (r *Ring) Next() *Ring {
|
||||
r.mu.Lock()
|
||||
r.ring = r.ring.Next()
|
||||
@ -120,11 +122,22 @@ func (r *Ring) Next() *Ring {
|
||||
return r
|
||||
}
|
||||
|
||||
// 连接两个环,两个环的大小和位置都有可能会发生改变。
|
||||
// 1、链接将环r与环s连接,使得r.Next()成为s并返回r.Next()的原始值。r一定不能为空。
|
||||
// 2、如果r和s指向同一个环,则链接它们会从环中移除r和s之间的元素。
|
||||
// 删除的元素形成子环,结果是对该子环的引用(如果没有删除元素,结果仍然是r.Next()的原始值,而不是nil)。
|
||||
// 3、如果r和s指向不同的环,则链接它们会创建一个单独的环,并在r之后插入s的元素。 结果指向插入后s的最后一个元素后面的元素。
|
||||
// Link connects ring r with ring s such that r.Next()
|
||||
// becomes s and returns the original value for r.Next().
|
||||
// r must not be empty.
|
||||
//
|
||||
// If r and s point to the same ring, linking
|
||||
// them removes the elements between r and s from the ring.
|
||||
// The removed elements form a subring and the result is a
|
||||
// reference to that subring (if no elements were removed,
|
||||
// the result is still the original value for r.Next(),
|
||||
// and not nil).
|
||||
//
|
||||
// If r and s point to different rings, linking
|
||||
// them creates a single ring with the elements of s inserted
|
||||
// after r. The result points to the element following the
|
||||
// last element of s after insertion.
|
||||
//
|
||||
func (r *Ring) Link(s *Ring) *Ring {
|
||||
r.mu.Lock()
|
||||
s.mu.Lock()
|
||||
@ -136,7 +149,10 @@ func (r *Ring) Link(s *Ring) *Ring {
|
||||
return r
|
||||
}
|
||||
|
||||
// 删除环中当前位置往后的n个数据项
|
||||
// Unlink removes n % r.Len() elements from the ring r, starting
|
||||
// at r.Next(). If n % r.Len() == 0, r remains unchanged.
|
||||
// The result is the removed subring. r must not be empty.
|
||||
//
|
||||
func (r *Ring) Unlink(n int) *Ring {
|
||||
r.mu.Lock()
|
||||
r.ring = r.ring.Unlink(n)
|
||||
@ -145,7 +161,9 @@ func (r *Ring) Unlink(n int) *Ring {
|
||||
return r
|
||||
}
|
||||
|
||||
// 读锁遍历,往后只读遍历,回调函数返回true表示继续遍历,否则退出遍历
|
||||
// RLockIteratorNext iterates and locks reading forward
|
||||
// with given callback function <f> within RWMutex.RLock.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (r *Ring) RLockIteratorNext(f func(value interface{}) bool) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
@ -159,7 +177,9 @@ func (r *Ring) RLockIteratorNext(f func(value interface{}) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// 读锁遍历,往前只读遍历,回调函数返回true表示继续遍历,否则退出遍历
|
||||
// RLockIteratorPrev iterates and locks reading backward
|
||||
// with given callback function <f> within RWMutex.RLock.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (r *Ring) RLockIteratorPrev(f func(value interface{}) bool) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
@ -173,7 +193,9 @@ func (r *Ring) RLockIteratorPrev(f func(value interface{}) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// 写锁遍历,往后写遍历,回调函数返回true表示继续遍历,否则退出遍历
|
||||
// LockIteratorNext iterates and locks writing forward
|
||||
// with given callback function <f> within RWMutex.RLock.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (r *Ring) LockIteratorNext(f func(item *ring.Ring) bool) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
@ -187,7 +209,9 @@ func (r *Ring) LockIteratorNext(f func(item *ring.Ring) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// 写锁遍历,往前写遍历,回调函数返回true表示继续遍历,否则退出遍历
|
||||
// LockIteratorPrev iterates and locks writing backward
|
||||
// with given callback function <f> within RWMutex.RLock.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (r *Ring) LockIteratorPrev(f func(item *ring.Ring) bool) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
@ -201,7 +225,7 @@ func (r *Ring) LockIteratorPrev(f func(item *ring.Ring) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// 从当前位置,往后只读完整遍历,返回非空数据项值构成的数组
|
||||
// SliceNext returns a copy of all item values as slice forward from current position.
|
||||
func (r *Ring) SliceNext() []interface{} {
|
||||
s := make([]interface{}, 0)
|
||||
r.mu.RLock()
|
||||
@ -217,7 +241,7 @@ func (r *Ring) SliceNext() []interface{} {
|
||||
return s
|
||||
}
|
||||
|
||||
// 从当前位置,往前只读完整遍历,返回非空数据项值构成的数组
|
||||
// SlicePrev returns a copy of all item values as slice backward from current position.
|
||||
func (r *Ring) SlicePrev() []interface{} {
|
||||
s := make([]interface{}, 0)
|
||||
r.mu.RLock()
|
||||
|
||||
@ -18,18 +18,14 @@ type Set struct {
|
||||
m map[interface{}]struct{}
|
||||
}
|
||||
|
||||
// Create a set, which contains un-repeated items.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的集合对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// New create and returns a new set, which contains un-repeated items.
|
||||
// The param <unsafe> used to specify whether using set in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func New(unsafe...bool) *Set {
|
||||
return NewSet(unsafe...)
|
||||
}
|
||||
|
||||
// See New.
|
||||
//
|
||||
// 同New.
|
||||
func NewSet(unsafe...bool) *Set {
|
||||
return &Set{
|
||||
m : make(map[interface{}]struct{}),
|
||||
@ -37,10 +33,21 @@ func NewSet(unsafe...bool) *Set {
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate the set by given callback <f>,
|
||||
// NewFrom returns a new set from <items>.
|
||||
// Parameter <items> can be either a variable of any type, or a slice.
|
||||
func NewFrom(items interface{}, unsafe...bool) *Set {
|
||||
m := make(map[interface{}]struct{})
|
||||
for _, v := range gconv.Interfaces(items) {
|
||||
m[v] = struct{}{}
|
||||
}
|
||||
return &Set{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the set with given callback function <f>,
|
||||
// if <f> returns true then continue iterating; or false to stop.
|
||||
//
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历。
|
||||
func (set *Set) Iterator(f func (v interface{}) bool) *Set {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
@ -52,9 +59,7 @@ func (set *Set) Iterator(f func (v interface{}) bool) *Set {
|
||||
return set
|
||||
}
|
||||
|
||||
// Add one or multiple items to the set.
|
||||
//
|
||||
// 添加元素项到集合中(支持多个).
|
||||
// Add adds one or multiple items to the set.
|
||||
func (set *Set) Add(item...interface{}) *Set {
|
||||
set.mu.Lock()
|
||||
for _, v := range item {
|
||||
@ -64,9 +69,7 @@ func (set *Set) Add(item...interface{}) *Set {
|
||||
return set
|
||||
}
|
||||
|
||||
// Check whether the set contains <item>.
|
||||
//
|
||||
// 键是否存在.
|
||||
// Contains checks whether the set contains <item>.
|
||||
func (set *Set) Contains(item interface{}) bool {
|
||||
set.mu.RLock()
|
||||
_, exists := set.m[item]
|
||||
@ -74,9 +77,7 @@ func (set *Set) Contains(item interface{}) bool {
|
||||
return exists
|
||||
}
|
||||
|
||||
// Remove <item> from set.
|
||||
//
|
||||
// 删除元素项。
|
||||
// Remove deletes <item> from set.
|
||||
func (set *Set) Remove(item interface{}) *Set {
|
||||
set.mu.Lock()
|
||||
delete(set.m, item)
|
||||
@ -84,9 +85,7 @@ func (set *Set) Remove(item interface{}) *Set {
|
||||
return set
|
||||
}
|
||||
|
||||
// Get size of the set.
|
||||
//
|
||||
// 获得集合大小。
|
||||
// Size returns the size of the set.
|
||||
func (set *Set) Size() int {
|
||||
set.mu.RLock()
|
||||
l := len(set.m)
|
||||
@ -94,9 +93,7 @@ func (set *Set) Size() int {
|
||||
return l
|
||||
}
|
||||
|
||||
// Clear the set.
|
||||
//
|
||||
// 清空集合。
|
||||
// Clear deletes all items of the set.
|
||||
func (set *Set) Clear() *Set {
|
||||
set.mu.Lock()
|
||||
set.m = make(map[interface{}]struct{})
|
||||
@ -104,9 +101,7 @@ func (set *Set) Clear() *Set {
|
||||
return set
|
||||
}
|
||||
|
||||
// Get the copy of items from set as slice.
|
||||
//
|
||||
// 获得集合元素项列表.
|
||||
// Slice returns the a of items of the set as slice.
|
||||
func (set *Set) Slice() []interface{} {
|
||||
set.mu.RLock()
|
||||
i := 0
|
||||
@ -119,43 +114,31 @@ func (set *Set) Slice() []interface{} {
|
||||
return ret
|
||||
}
|
||||
|
||||
// Join set items with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前集合的元素项,构造成新的字符串返回。
|
||||
// Join joins items with a string <glue>.
|
||||
func (set *Set) Join(glue string) string {
|
||||
return strings.Join(gconv.Strings(set.Slice()), ",")
|
||||
}
|
||||
|
||||
// Return set items as a string, which are joined by char ','.
|
||||
//
|
||||
// 使用glue字符串串连当前集合的元素项,构造成新的字符串返回。
|
||||
// String returns items as a string, which are joined by char ','.
|
||||
func (set *Set) String() string {
|
||||
return set.Join(",")
|
||||
}
|
||||
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (set *Set) LockFunc(f func(m map[interface{}]struct{})) *Set {
|
||||
// LockFunc locks writing with callback function <f>.
|
||||
func (set *Set) LockFunc(f func(m map[interface{}]struct{})) {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (set *Set) RLockFunc(f func(m map[interface{}]struct{})) *Set {
|
||||
// RLockFunc locks reading with callback function <f>.
|
||||
func (set *Set) RLockFunc(f func(m map[interface{}]struct{})) {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
|
||||
// Check whether the two sets equal.
|
||||
//
|
||||
// 判断两个集合是否相等.
|
||||
// Equal checks whether the two sets equal.
|
||||
func (set *Set) Equal(other *Set) bool {
|
||||
if set == other {
|
||||
return true
|
||||
@ -175,9 +158,7 @@ func (set *Set) Equal(other *Set) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check whether the current set is sub-set of <other>.
|
||||
//
|
||||
// 判断当前集合是否为other集合的子集.
|
||||
// IsSubsetOf checks whether the current set is a sub-set of <other>.
|
||||
func (set *Set) IsSubsetOf(other *Set) bool {
|
||||
if set == other {
|
||||
return true
|
||||
@ -194,10 +175,8 @@ func (set *Set) IsSubsetOf(other *Set) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Returns a new set which is the union of <set> and <other>.
|
||||
// Which means, all the items in <newSet> is in <set> or in <other>.
|
||||
//
|
||||
// 并集, 返回新的集合:属于set或属于others的元素为元素的集合.
|
||||
// Union returns a new set which is the union of <set> and <others>.
|
||||
// Which means, all the items in <newSet> are in <set> or in <others>.
|
||||
func (set *Set) Union(others ... *Set) (newSet *Set) {
|
||||
newSet = NewSet(true)
|
||||
set.mu.RLock()
|
||||
@ -222,10 +201,8 @@ func (set *Set) Union(others ... *Set) (newSet *Set) {
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the difference set from <set> to <other>.
|
||||
// Which means, all the items in <newSet> is in <set> and not in <other>.
|
||||
//
|
||||
// 差集, 返回新的集合: 属于set且不属于others的元素为元素的集合.
|
||||
// Diff returns a new set which is the difference set from <set> to <others>.
|
||||
// Which means, all the items in <newSet> are in <set> but not in <others>.
|
||||
func (set *Set) Diff(others...*Set) (newSet *Set) {
|
||||
newSet = NewSet(true)
|
||||
set.mu.RLock()
|
||||
@ -245,10 +222,8 @@ func (set *Set) Diff(others...*Set) (newSet *Set) {
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the intersection from <set> to <other>.
|
||||
// Which means, all the items in <newSet> is in <set> and also in <other>.
|
||||
//
|
||||
// 交集, 返回新的集合: 属于set且属于others的元素为元素的集合.
|
||||
// Intersect returns a new set which is the intersection from <set> to <others>.
|
||||
// Which means, all the items in <newSet> are in <set> and also in <others>.
|
||||
func (set *Set) Intersect(others...*Set) (newSet *Set) {
|
||||
newSet = NewSet(true)
|
||||
set.mu.RLock()
|
||||
@ -269,11 +244,11 @@ func (set *Set) Intersect(others...*Set) (newSet *Set) {
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the complement from <set> to <full>.
|
||||
// Which means, all the items in <newSet> is in <full> and not in <set>.
|
||||
// Complement returns a new set which is the complement from <set> to <full>.
|
||||
// Which means, all the items in <newSet> are in <full> and not in <set>.
|
||||
//
|
||||
// 补集, 返回新的集合: (前提: set应当为full的子集)属于全集full不属于集合set的元素组成的集合.
|
||||
// 如果给定的full集合不是set的全集时,返回full与set的差集.
|
||||
// It returns the difference between <full> and <set>
|
||||
// if the given set <full> is not the full set of <set>.
|
||||
func (set *Set) Complement(full *Set) (newSet *Set) {
|
||||
newSet = NewSet(true)
|
||||
set.mu.RLock()
|
||||
@ -288,4 +263,63 @@ func (set *Set) Complement(full *Set) (newSet *Set) {
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Merge adds items from <others> sets into <set>.
|
||||
func (set *Set) Merge(others ... *Set) *Set {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
for _, other := range others {
|
||||
if set != other {
|
||||
other.mu.RLock()
|
||||
}
|
||||
for k, v := range other.m {
|
||||
set.m[k] = v
|
||||
}
|
||||
if set != other {
|
||||
other.mu.RUnlock()
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// Sum sums items.
|
||||
// Note: The items should be converted to int type,
|
||||
// or you'd get a result that you unexpected.
|
||||
func (set *Set) Sum() (sum int) {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for k, _ := range set.m {
|
||||
sum += gconv.Int(k)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops randomly pops an item from set.
|
||||
func (set *Set) Pop(size int) interface{} {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for k, _ := range set.m {
|
||||
return k
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pops randomly pops <size> items from set.
|
||||
func (set *Set) Pops(size int) []interface{} {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
if size > len(set.m) {
|
||||
size = len(set.m)
|
||||
}
|
||||
index := 0
|
||||
array := make([]interface{}, size)
|
||||
for k, _ := range set.m {
|
||||
array[index] = k
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return array
|
||||
}
|
||||
@ -18,11 +18,9 @@ type IntSet struct {
|
||||
m map[int]struct{}
|
||||
}
|
||||
|
||||
// Create a set, which contains un-repeated items.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的集合对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// New create and returns a new set, which contains un-repeated items.
|
||||
// The param <unsafe> used to specify whether using set in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntSet(unsafe...bool) *IntSet {
|
||||
return &IntSet{
|
||||
m : make(map[int]struct{}),
|
||||
@ -30,10 +28,20 @@ func NewIntSet(unsafe...bool) *IntSet {
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate the set by given callback <f>,
|
||||
// NewIntSetFrom returns a new set from <items>.
|
||||
func NewIntSetFrom(items []int, unsafe...bool) *IntSet {
|
||||
m := make(map[int]struct{})
|
||||
for _, v := range items {
|
||||
m[v] = struct{}{}
|
||||
}
|
||||
return &IntSet{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the set with given callback function <f>,
|
||||
// if <f> returns true then continue iterating; or false to stop.
|
||||
//
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历。
|
||||
func (set *IntSet) Iterator(f func (v int) bool) *IntSet {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
@ -45,9 +53,7 @@ func (set *IntSet) Iterator(f func (v int) bool) *IntSet {
|
||||
return set
|
||||
}
|
||||
|
||||
// Add one or multiple items to the set.
|
||||
//
|
||||
// 添加元素项到集合中(支持多个).
|
||||
// Add adds one or multiple items to the set.
|
||||
func (set *IntSet) Add(item...int) *IntSet {
|
||||
set.mu.Lock()
|
||||
for _, v := range item {
|
||||
@ -57,9 +63,7 @@ func (set *IntSet) Add(item...int) *IntSet {
|
||||
return set
|
||||
}
|
||||
|
||||
// Check whether the set contains <item>.
|
||||
//
|
||||
// 键是否存在.
|
||||
// Contains checks whether the set contains <item>.
|
||||
func (set *IntSet) Contains(item int) bool {
|
||||
set.mu.RLock()
|
||||
_, exists := set.m[item]
|
||||
@ -67,9 +71,7 @@ func (set *IntSet) Contains(item int) bool {
|
||||
return exists
|
||||
}
|
||||
|
||||
// Remove <item> from set.
|
||||
//
|
||||
// 删除元素项。
|
||||
// Remove deletes <item> from set.
|
||||
func (set *IntSet) Remove(item int) *IntSet {
|
||||
set.mu.Lock()
|
||||
delete(set.m, item)
|
||||
@ -77,9 +79,7 @@ func (set *IntSet) Remove(item int) *IntSet {
|
||||
return set
|
||||
}
|
||||
|
||||
// Get size of the set.
|
||||
//
|
||||
// 获得集合大小。
|
||||
// Size returns the size of the set.
|
||||
func (set *IntSet) Size() int {
|
||||
set.mu.RLock()
|
||||
l := len(set.m)
|
||||
@ -87,9 +87,7 @@ func (set *IntSet) Size() int {
|
||||
return l
|
||||
}
|
||||
|
||||
// Clear the set.
|
||||
//
|
||||
// 清空集合。
|
||||
// Clear deletes all items of the set.
|
||||
func (set *IntSet) Clear() *IntSet {
|
||||
set.mu.Lock()
|
||||
set.m = make(map[int]struct{})
|
||||
@ -97,9 +95,7 @@ func (set *IntSet) Clear() *IntSet {
|
||||
return set
|
||||
}
|
||||
|
||||
// Get the copy of items from set as slice.
|
||||
//
|
||||
// 获得集合元素项列表.
|
||||
// Slice returns the a of items of the set as slice.
|
||||
func (set *IntSet) Slice() []int {
|
||||
set.mu.RLock()
|
||||
ret := make([]int, len(set.m))
|
||||
@ -112,43 +108,31 @@ func (set *IntSet) Slice() []int {
|
||||
return ret
|
||||
}
|
||||
|
||||
// Join set items with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前集合的元素项,构造成新的字符串返回。
|
||||
// Join joins items with a string <glue>.
|
||||
func (set *IntSet) Join(glue string) string {
|
||||
return strings.Join(gconv.Strings(set.Slice()), ",")
|
||||
}
|
||||
|
||||
// Return set items as a string, which are joined by char ','.
|
||||
//
|
||||
// 使用glue字符串串连当前集合的元素项,构造成新的字符串返回。
|
||||
// String returns items as a string, which are joined by char ','.
|
||||
func (set *IntSet) String() string {
|
||||
return set.Join(",")
|
||||
}
|
||||
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (set *IntSet) LockFunc(f func(m map[int]struct{})) *IntSet {
|
||||
// LockFunc locks writing with callback function <f>.
|
||||
func (set *IntSet) LockFunc(f func(m map[int]struct{})) {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (set *IntSet) RLockFunc(f func(m map[int]struct{})) *IntSet {
|
||||
// RLockFunc locks reading with callback function <f>.
|
||||
func (set *IntSet) RLockFunc(f func(m map[int]struct{})) {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
|
||||
// Check whether the two sets equal.
|
||||
//
|
||||
// 判断两个集合是否相等.
|
||||
// Equal checks whether the two sets equal.
|
||||
func (set *IntSet) Equal(other *IntSet) bool {
|
||||
if set == other {
|
||||
return true
|
||||
@ -168,9 +152,7 @@ func (set *IntSet) Equal(other *IntSet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check whether the current set is sub-set of <other>.
|
||||
//
|
||||
// 判断当前集合是否为other集合的子集.
|
||||
// IsSubsetOf checks whether the current set is a sub-set of <other>.
|
||||
func (set *IntSet) IsSubsetOf(other *IntSet) bool {
|
||||
if set == other {
|
||||
return true
|
||||
@ -187,10 +169,8 @@ func (set *IntSet) IsSubsetOf(other *IntSet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Returns a new set which is the union of <set> and <other>.
|
||||
// Which means, all the items in <newSet> is in <set> or in <other>.
|
||||
//
|
||||
// 并集, 返回新的集合:属于set或属于others的元素为元素的集合.
|
||||
// Union returns a new set which is the union of <set> and <other>.
|
||||
// Which means, all the items in <newSet> are in <set> or in <other>.
|
||||
func (set *IntSet) Union(others ... *IntSet) (newSet *IntSet) {
|
||||
newSet = NewIntSet(true)
|
||||
set.mu.RLock()
|
||||
@ -215,10 +195,8 @@ func (set *IntSet) Union(others ... *IntSet) (newSet *IntSet) {
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the difference set from <set> to <other>.
|
||||
// Which means, all the items in <newSet> is in <set> and not in <other>.
|
||||
//
|
||||
// 差集, 返回新的集合: 属于set且不属于others的元素为元素的集合.
|
||||
// Diff returns a new set which is the difference set from <set> to <other>.
|
||||
// Which means, all the items in <newSet> are in <set> but not in <other>.
|
||||
func (set *IntSet) Diff(others...*IntSet) (newSet *IntSet) {
|
||||
newSet = NewIntSet(true)
|
||||
set.mu.RLock()
|
||||
@ -238,10 +216,8 @@ func (set *IntSet) Diff(others...*IntSet) (newSet *IntSet) {
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the intersection from <set> to <other>.
|
||||
// Which means, all the items in <newSet> is in <set> and also in <other>.
|
||||
//
|
||||
// 交集, 返回新的集合: 属于set且属于others的元素为元素的集合.
|
||||
// Intersect returns a new set which is the intersection from <set> to <other>.
|
||||
// Which means, all the items in <newSet> are in <set> and also in <other>.
|
||||
func (set *IntSet) Intersect(others...*IntSet) (newSet *IntSet) {
|
||||
newSet = NewIntSet(true)
|
||||
set.mu.RLock()
|
||||
@ -262,11 +238,11 @@ func (set *IntSet) Intersect(others...*IntSet) (newSet *IntSet) {
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the complement from <set> to <full>.
|
||||
// Which means, all the items in <newSet> is in <full> and not in <set>.
|
||||
// Complement returns a new set which is the complement from <set> to <full>.
|
||||
// Which means, all the items in <newSet> are in <full> and not in <set>.
|
||||
//
|
||||
// 补集, 返回新的集合: (前提: set应当为full的子集)属于全集full不属于集合set的元素组成的集合.
|
||||
// 如果给定的full集合不是set的全集时,返回full与set的差集.
|
||||
// It returns the difference between <full> and <set>
|
||||
// if the given set <full> is not the full set of <set>.
|
||||
func (set *IntSet) Complement(full *IntSet) (newSet *IntSet) {
|
||||
newSet = NewIntSet(true)
|
||||
set.mu.RLock()
|
||||
@ -282,3 +258,62 @@ func (set *IntSet) Complement(full *IntSet) (newSet *IntSet) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Merge adds items from <others> sets into <set>.
|
||||
func (set *IntSet) Merge(others ... *IntSet) *IntSet {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
for _, other := range others {
|
||||
if set != other {
|
||||
other.mu.RLock()
|
||||
}
|
||||
for k, v := range other.m {
|
||||
set.m[k] = v
|
||||
}
|
||||
if set != other {
|
||||
other.mu.RUnlock()
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// Sum sums items.
|
||||
// Note: The items should be converted to int type,
|
||||
// or you'd get a result that you unexpected.
|
||||
func (set *IntSet) Sum() (sum int) {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for k, _ := range set.m {
|
||||
sum += k
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops randomly pops an item from set.
|
||||
func (set *IntSet) Pop(size int) int {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for k, _ := range set.m {
|
||||
return k
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Pops randomly pops <size> items from set.
|
||||
func (set *IntSet) Pops(size int) []int {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
if size > len(set.m) {
|
||||
size = len(set.m)
|
||||
}
|
||||
index := 0
|
||||
array := make([]int, size)
|
||||
for k, _ := range set.m {
|
||||
array[index] = k
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return array
|
||||
}
|
||||
@ -9,7 +9,8 @@ package gset
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"strings"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type StringSet struct {
|
||||
@ -17,11 +18,9 @@ type StringSet struct {
|
||||
m map[string]struct{}
|
||||
}
|
||||
|
||||
// Create a set, which contains un-repeated items.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的集合对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// New create and returns a new set, which contains un-repeated items.
|
||||
// The param <unsafe> used to specify whether using set in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStringSet(unsafe...bool) *StringSet {
|
||||
return &StringSet {
|
||||
m : make(map[string]struct{}),
|
||||
@ -29,10 +28,20 @@ func NewStringSet(unsafe...bool) *StringSet {
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate the set by given callback <f>,
|
||||
// NewStringSetFrom returns a new set from <items>.
|
||||
func NewStringSetFrom(items []string, unsafe...bool) *StringSet {
|
||||
m := make(map[string]struct{})
|
||||
for _, v := range items {
|
||||
m[v] = struct{}{}
|
||||
}
|
||||
return &StringSet{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the set with given callback function <f>,
|
||||
// if <f> returns true then continue iterating; or false to stop.
|
||||
//
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历。
|
||||
func (set *StringSet) Iterator(f func (v string) bool) *StringSet {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
@ -44,9 +53,7 @@ func (set *StringSet) Iterator(f func (v string) bool) *StringSet {
|
||||
return set
|
||||
}
|
||||
|
||||
// Add one or multiple items to the set.
|
||||
//
|
||||
// 添加元素项到集合中(支持多个).
|
||||
// Add adds one or multiple items to the set.
|
||||
func (set *StringSet) Add(item...string) *StringSet {
|
||||
set.mu.Lock()
|
||||
for _, v := range item {
|
||||
@ -56,9 +63,7 @@ func (set *StringSet) Add(item...string) *StringSet {
|
||||
return set
|
||||
}
|
||||
|
||||
// Check whether the set contains <item>.
|
||||
//
|
||||
// 键是否存在.
|
||||
// Contains checks whether the set contains <item>.
|
||||
func (set *StringSet) Contains(item string) bool {
|
||||
set.mu.RLock()
|
||||
_, exists := set.m[item]
|
||||
@ -66,9 +71,7 @@ func (set *StringSet) Contains(item string) bool {
|
||||
return exists
|
||||
}
|
||||
|
||||
// Remove <item> from set.
|
||||
//
|
||||
// 删除元素项。
|
||||
// Remove deletes <item> from set.
|
||||
func (set *StringSet) Remove(item string) *StringSet {
|
||||
set.mu.Lock()
|
||||
delete(set.m, item)
|
||||
@ -76,9 +79,7 @@ func (set *StringSet) Remove(item string) *StringSet {
|
||||
return set
|
||||
}
|
||||
|
||||
// Get size of the set.
|
||||
//
|
||||
// 获得集合大小。
|
||||
// Size returns the size of the set.
|
||||
func (set *StringSet) Size() int {
|
||||
set.mu.RLock()
|
||||
l := len(set.m)
|
||||
@ -86,9 +87,7 @@ func (set *StringSet) Size() int {
|
||||
return l
|
||||
}
|
||||
|
||||
// Clear the set.
|
||||
//
|
||||
// 清空集合。
|
||||
// Clear deletes all items of the set.
|
||||
func (set *StringSet) Clear() *StringSet {
|
||||
set.mu.Lock()
|
||||
set.m = make(map[string]struct{})
|
||||
@ -96,9 +95,7 @@ func (set *StringSet) Clear() *StringSet {
|
||||
return set
|
||||
}
|
||||
|
||||
// Get the copy of items from set as slice.
|
||||
//
|
||||
// 获得集合元素项列表.
|
||||
// Slice returns the a of items of the set as slice.
|
||||
func (set *StringSet) Slice() []string {
|
||||
set.mu.RLock()
|
||||
ret := make([]string, len(set.m))
|
||||
@ -112,43 +109,31 @@ func (set *StringSet) Slice() []string {
|
||||
return ret
|
||||
}
|
||||
|
||||
// Join set items with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前集合的元素项,构造成新的字符串返回。
|
||||
// Join joins items with a string <glue>.
|
||||
func (set *StringSet) Join(glue string) string {
|
||||
return strings.Join(set.Slice(), ",")
|
||||
}
|
||||
|
||||
// Return set items as a string, which are joined by char ','.
|
||||
//
|
||||
// 使用glue字符串串连当前集合的元素项,构造成新的字符串返回。
|
||||
// String returns items as a string, which are joined by char ','.
|
||||
func (set *StringSet) String() string {
|
||||
return set.Join(",")
|
||||
}
|
||||
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (set *StringSet) LockFunc(f func(m map[string]struct{})) *StringSet {
|
||||
// LockFunc locks writing with callback function <f>.
|
||||
func (set *StringSet) LockFunc(f func(m map[string]struct{})) {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (set *StringSet) RLockFunc(f func(m map[string]struct{})) *StringSet {
|
||||
// RLockFunc locks reading with callback function <f>.
|
||||
func (set *StringSet) RLockFunc(f func(m map[string]struct{})) {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
|
||||
// Check whether the two sets equal.
|
||||
//
|
||||
// 判断两个集合是否相等.
|
||||
// Equal checks whether the two sets equal.
|
||||
func (set *StringSet) Equal(other *StringSet) bool {
|
||||
if set == other {
|
||||
return true
|
||||
@ -168,9 +153,7 @@ func (set *StringSet) Equal(other *StringSet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check whether the current set is sub-set of <other>.
|
||||
//
|
||||
// 判断当前集合是否为other集合的子集.
|
||||
// IsSubsetOf checks whether the current set is a sub-set of <other>.
|
||||
func (set *StringSet) IsSubsetOf(other *StringSet) bool {
|
||||
if set == other {
|
||||
return true
|
||||
@ -187,10 +170,8 @@ func (set *StringSet) IsSubsetOf(other *StringSet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Returns a new set which is the union of <set> and <other>.
|
||||
// Which means, all the items in <newSet> is in <set> or in <other>.
|
||||
//
|
||||
// 并集, 返回新的集合:属于set或属于others的元素为元素的集合.
|
||||
// Union returns a new set which is the union of <set> and <other>.
|
||||
// Which means, all the items in <newSet> are in <set> or in <other>.
|
||||
func (set *StringSet) Union(others ... *StringSet) (newSet *StringSet) {
|
||||
newSet = NewStringSet(true)
|
||||
set.mu.RLock()
|
||||
@ -215,10 +196,8 @@ func (set *StringSet) Union(others ... *StringSet) (newSet *StringSet) {
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the difference set from <set> to <other>.
|
||||
// Which means, all the items in <newSet> is in <set> and not in <other>.
|
||||
//
|
||||
// 差集, 返回新的集合: 属于set且不属于others的元素为元素的集合.
|
||||
// Diff returns a new set which is the difference set from <set> to <other>.
|
||||
// Which means, all the items in <newSet> are in <set> but not in <other>.
|
||||
func (set *StringSet) Diff(others...*StringSet) (newSet *StringSet) {
|
||||
newSet = NewStringSet(true)
|
||||
set.mu.RLock()
|
||||
@ -238,10 +217,8 @@ func (set *StringSet) Diff(others...*StringSet) (newSet *StringSet) {
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the intersection from <set> to <other>.
|
||||
// Which means, all the items in <newSet> is in <set> and also in <other>.
|
||||
//
|
||||
// 交集, 返回新的集合: 属于set且属于others的元素为元素的集合.
|
||||
// Intersect returns a new set which is the intersection from <set> to <other>.
|
||||
// Which means, all the items in <newSet> are in <set> and also in <other>.
|
||||
func (set *StringSet) Intersect(others...*StringSet) (newSet *StringSet) {
|
||||
newSet = NewStringSet(true)
|
||||
set.mu.RLock()
|
||||
@ -262,11 +239,11 @@ func (set *StringSet) Intersect(others...*StringSet) (newSet *StringSet) {
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the complement from <set> to <full>.
|
||||
// Which means, all the items in <newSet> is in <full> and not in <set>.
|
||||
// Complement returns a new set which is the complement from <set> to <full>.
|
||||
// Which means, all the items in <newSet> are in <full> and not in <set>.
|
||||
//
|
||||
// 补集, 返回新的集合: (前提: set应当为full的子集)属于全集full不属于集合set的元素组成的集合.
|
||||
// 如果给定的full集合不是set的全集时,返回full与set的差集.
|
||||
// It returns the difference between <full> and <set>
|
||||
// if the given set <full> is not the full set of <set>.
|
||||
func (set *StringSet) Complement(full *StringSet) (newSet *StringSet) {
|
||||
newSet = NewStringSet(true)
|
||||
set.mu.RLock()
|
||||
@ -282,3 +259,62 @@ func (set *StringSet) Complement(full *StringSet) (newSet *StringSet) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Merge adds items from <others> sets into <set>.
|
||||
func (set *StringSet) Merge(others ... *StringSet) *StringSet {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
for _, other := range others {
|
||||
if set != other {
|
||||
other.mu.RLock()
|
||||
}
|
||||
for k, v := range other.m {
|
||||
set.m[k] = v
|
||||
}
|
||||
if set != other {
|
||||
other.mu.RUnlock()
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// Sum sums items.
|
||||
// Note: The items should be converted to int type,
|
||||
// or you'd get a result that you unexpected.
|
||||
func (set *StringSet) Sum() (sum int) {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for k, _ := range set.m {
|
||||
sum += gconv.Int(k)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops randomly pops an item from set.
|
||||
func (set *StringSet) Pop(size int) string {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for k, _ := range set.m {
|
||||
return k
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Pops randomly pops <size> items from set.
|
||||
func (set *StringSet) Pops(size int) []string {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
if size > len(set.m) {
|
||||
size = len(set.m)
|
||||
}
|
||||
index := 0
|
||||
array := make([]string, size)
|
||||
for k, _ := range set.m {
|
||||
array[index] = k
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return array
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap
|
||||
|
||||
// Package gtree provides concurrent-safe/unsafe tree containers.
|
||||
package gtree
|
||||
701
g/container/gtree/gtree_avltree.go
Normal file
701
g/container/gtree/gtree_avltree.go
Normal file
@ -0,0 +1,701 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). 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 gtree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
// AVLTree holds elements of the AVL tree.
|
||||
type AVLTree struct {
|
||||
mu *rwmutex.RWMutex
|
||||
root *AVLTreeNode
|
||||
comparator func(v1, v2 interface{}) int
|
||||
size int
|
||||
}
|
||||
|
||||
// AVLTreeNode is a single element within the tree.
|
||||
type AVLTreeNode struct {
|
||||
Key interface{}
|
||||
Value interface{}
|
||||
parent *AVLTreeNode
|
||||
children [2]*AVLTreeNode
|
||||
b int8
|
||||
}
|
||||
|
||||
// NewAVLTree instantiates an AVL tree with the custom comparator.
|
||||
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewAVLTree(comparator func(v1, v2 interface{}) int, unsafe...bool) *AVLTree {
|
||||
return &AVLTree{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
comparator: comparator,
|
||||
}
|
||||
}
|
||||
|
||||
// NewAVLTreeFrom instantiates an AVL tree with the custom comparator and data map.
|
||||
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewAVLTreeFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe...bool) *AVLTree {
|
||||
tree := NewAVLTree(comparator, unsafe...)
|
||||
for k, v := range data {
|
||||
tree.put(k, v, nil, &tree.root)
|
||||
}
|
||||
return tree
|
||||
}
|
||||
|
||||
// Clone returns a new tree with a copy of current tree.
|
||||
func (tree *AVLTree) Clone(unsafe ...bool) *AVLTree {
|
||||
newTree := NewAVLTree(tree.comparator, !tree.mu.IsSafe())
|
||||
newTree.Sets(tree.Map())
|
||||
return newTree
|
||||
}
|
||||
|
||||
// Set inserts node into the tree.
|
||||
func (tree *AVLTree) Set(key interface{}, value interface{}) {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
tree.put(key, value, nil, &tree.root)
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the tree.
|
||||
func (tree *AVLTree) Sets(data map[interface{}]interface{}) {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
for key, value := range data {
|
||||
tree.put(key, value, nil, &tree.root)
|
||||
}
|
||||
}
|
||||
|
||||
// Search searches the tree with given <key>.
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (tree *AVLTree) Search(key interface{}) (value interface{}, found bool) {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
return tree.doSearch(key)
|
||||
}
|
||||
|
||||
// doSearch searches the tree with given <key>.
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (tree *AVLTree) doSearch(key interface{}) (value interface{}, found bool) {
|
||||
n := tree.root
|
||||
for n != nil {
|
||||
cmp := tree.comparator(key, n.Key)
|
||||
switch {
|
||||
case cmp == 0: return n.Value, true
|
||||
case cmp < 0: n = n.children[0]
|
||||
case cmp > 0: n = n.children[1]
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Get searches the node in the tree by <key> and returns its value or nil if key is not found in tree.
|
||||
func (tree *AVLTree) Get(key interface{}) (value interface{}) {
|
||||
value, _ = tree.Search(key)
|
||||
return
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the hash map,
|
||||
// and its return value will be set to the map with <key>.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (tree *AVLTree) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
if v, ok := tree.doSearch(key); ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface {}); ok {
|
||||
value = f()
|
||||
}
|
||||
tree.put(key, value, nil, &tree.root)
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (tree *AVLTree) GetOrSet(key interface{}, value interface{}) interface{} {
|
||||
if v, ok := tree.Search(key); !ok {
|
||||
return tree.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (tree *AVLTree) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
|
||||
if v, ok := tree.Search(key); !ok {
|
||||
return tree.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (tree *AVLTree) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
|
||||
if v, ok := tree.Search(key); !ok {
|
||||
return tree.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with the value by given <key>.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (tree *AVLTree) GetVar(key interface{}) *gvar.Var {
|
||||
return gvar.New(tree.Get(key), true)
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (tree *AVLTree) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
|
||||
return gvar.New(tree.GetOrSet(key, value), true)
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (tree *AVLTree) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(tree.GetOrSetFunc(key, f), true)
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (tree *AVLTree) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(tree.GetOrSetFuncLock(key, f), true)
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (tree *AVLTree) SetIfNotExist(key interface{}, value interface{}) bool {
|
||||
if !tree.Contains(key) {
|
||||
tree.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (tree *AVLTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
|
||||
if !tree.Contains(key) {
|
||||
tree.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (tree *AVLTree) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
|
||||
if !tree.Contains(key) {
|
||||
tree.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Contains checks whether <key> exists in the tree.
|
||||
func (tree *AVLTree) Contains(key interface{}) bool {
|
||||
_, ok := tree.Search(key)
|
||||
return ok
|
||||
}
|
||||
|
||||
// Remove remove the node from the tree by key.
|
||||
// Key should adhere to the comparator's type assertion, otherwise method panics.
|
||||
func (tree *AVLTree) Remove(key interface{}) (value interface{}) {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
value, _ = tree.remove(key, &tree.root)
|
||||
return
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the tree by <keys>.
|
||||
func (tree *AVLTree) Removes(keys []interface{}) {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
for _, key := range keys {
|
||||
tree.remove(key, &tree.root)
|
||||
}
|
||||
}
|
||||
|
||||
// IsEmpty returns true if tree does not contain any nodes.
|
||||
func (tree *AVLTree) IsEmpty() bool {
|
||||
return tree.Size() == 0
|
||||
}
|
||||
|
||||
// Size returns number of nodes in the tree.
|
||||
func (tree *AVLTree) Size() int {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
return tree.size
|
||||
}
|
||||
|
||||
// Keys returns all keys in asc order.
|
||||
func (tree *AVLTree) Keys() []interface{} {
|
||||
keys := make([]interface{}, tree.Size())
|
||||
index := 0
|
||||
tree.IteratorAsc(func(key, value interface{}) bool {
|
||||
keys[index] = key
|
||||
index++
|
||||
return true
|
||||
})
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values in asc order based on the key.
|
||||
func (tree *AVLTree) Values() []interface{} {
|
||||
values := make([]interface{}, tree.Size())
|
||||
index := 0
|
||||
tree.IteratorAsc(func(key, value interface{}) bool {
|
||||
values[index] = value
|
||||
index++
|
||||
return true
|
||||
})
|
||||
return values
|
||||
}
|
||||
|
||||
// Left returns the minimum element of the AVL tree
|
||||
// or nil if the tree is empty.
|
||||
func (tree *AVLTree) Left() *AVLTreeNode {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
node := tree.bottom(0)
|
||||
if tree.mu.IsSafe() {
|
||||
return &AVLTreeNode {
|
||||
Key : node.Key,
|
||||
Value : node.Value,
|
||||
}
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
// Right returns the maximum element of the AVL tree
|
||||
// or nil if the tree is empty.
|
||||
func (tree *AVLTree) Right() *AVLTreeNode {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
node := tree.bottom(1)
|
||||
if tree.mu.IsSafe() {
|
||||
return &AVLTreeNode {
|
||||
Key : node.Key,
|
||||
Value : node.Value,
|
||||
}
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
// Floor Finds floor node of the input key, return the floor node or nil if no ceiling is found.
|
||||
// Second return parameter is true if floor was found, otherwise false.
|
||||
//
|
||||
// Floor node is defined as the largest node that is smaller than or equal to the given node.
|
||||
// A floor node may not be found, either because the tree is empty, or because
|
||||
// all nodes in the tree is larger than the given node.
|
||||
//
|
||||
// Key should adhere to the comparator's type assertion, otherwise method panics.
|
||||
func (tree *AVLTree) Floor(key interface{}) (floor *AVLTreeNode, found bool) {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
found = false
|
||||
n := tree.root
|
||||
for n != nil {
|
||||
c := tree.comparator(key, n.Key)
|
||||
switch {
|
||||
case c == 0: return n, true
|
||||
case c < 0: n = n.children[0]
|
||||
case c > 0:
|
||||
floor, found = n, true
|
||||
n = n.children[1]
|
||||
}
|
||||
}
|
||||
if found {
|
||||
return
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Ceiling finds ceiling node of the input key, return the ceiling node or nil if no ceiling is found.
|
||||
// Second return parameter is true if ceiling was found, otherwise false.
|
||||
//
|
||||
// Ceiling node is defined as the smallest node that is larger than or equal to the given node.
|
||||
// A ceiling node may not be found, either because the tree is empty, or because
|
||||
// all nodes in the tree is smaller than the given node.
|
||||
//
|
||||
// Key should adhere to the comparator's type assertion, otherwise method panics.
|
||||
func (tree *AVLTree) Ceiling(key interface{}) (floor *AVLTreeNode, found bool) {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
found = false
|
||||
n := tree.root
|
||||
for n != nil {
|
||||
c := tree.comparator(key, n.Key)
|
||||
switch {
|
||||
case c == 0: return n, true
|
||||
case c > 0: n = n.children[1]
|
||||
case c < 0:
|
||||
floor, found = n, true
|
||||
n = n.children[0]
|
||||
}
|
||||
}
|
||||
if found {
|
||||
return
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Clear removes all nodes from the tree.
|
||||
func (tree *AVLTree) Clear() {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
tree.root = nil
|
||||
tree.size = 0
|
||||
}
|
||||
|
||||
// String returns a string representation of container
|
||||
func (tree *AVLTree) String() string {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
str := "AVLTree\n"
|
||||
if tree.size != 0 {
|
||||
output(tree.root, "", true, &str)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// Print prints the tree to stdout.
|
||||
func (tree *AVLTree) Print() {
|
||||
fmt.Println(tree.String())
|
||||
}
|
||||
|
||||
// Map returns all key-value items as map.
|
||||
func (tree *AVLTree) Map() map[interface{}]interface{} {
|
||||
m := make(map[interface{}]interface{}, tree.Size())
|
||||
tree.IteratorAsc(func(key, value interface{}) bool {
|
||||
m[key] = value
|
||||
return true
|
||||
})
|
||||
return m
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the tree to value-key.
|
||||
// Note that you should guarantee the value is the same type as key,
|
||||
// or else the comparator would panic.
|
||||
//
|
||||
// If the type of value is different with key, you pass the new <comparator>.
|
||||
func (tree *AVLTree) Flip(comparator...func(v1, v2 interface{}) int) {
|
||||
t := (*AVLTree)(nil)
|
||||
if len(comparator) > 0 {
|
||||
t = NewAVLTree(comparator[0], !tree.mu.IsSafe())
|
||||
} else {
|
||||
t = NewAVLTree(tree.comparator, !tree.mu.IsSafe())
|
||||
}
|
||||
tree.IteratorAsc(func(key, value interface{}) bool {
|
||||
t.put(value, key, nil, &t.root)
|
||||
return true
|
||||
})
|
||||
tree.mu.Lock()
|
||||
tree.root = t.root
|
||||
tree.size = t.size
|
||||
tree.mu.Unlock()
|
||||
}
|
||||
|
||||
// Iterator is alias of IteratorAsc.
|
||||
func (tree *AVLTree) Iterator(f func (key, value interface{}) bool) {
|
||||
tree.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the tree in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (tree *AVLTree) IteratorAsc(f func (key, value interface{}) bool) {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
node := tree.bottom(0)
|
||||
for node != nil {
|
||||
if !f(node.Key, node.Value) {
|
||||
return
|
||||
}
|
||||
node = node.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the tree in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (tree *AVLTree) IteratorDesc(f func (key, value interface{}) bool) {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
node := tree.bottom(1)
|
||||
for node != nil {
|
||||
if !f(node.Key, node.Value) {
|
||||
return
|
||||
}
|
||||
node = node.Prev()
|
||||
}
|
||||
}
|
||||
|
||||
func (tree *AVLTree) put(key interface{}, value interface{}, p *AVLTreeNode, qp **AVLTreeNode) bool {
|
||||
q := *qp
|
||||
if q == nil {
|
||||
tree.size++
|
||||
*qp = &AVLTreeNode{Key: key, Value: value, parent: p}
|
||||
return true
|
||||
}
|
||||
|
||||
c := tree.comparator(key, q.Key)
|
||||
if c == 0 {
|
||||
q.Key = key
|
||||
q.Value = value
|
||||
return false
|
||||
}
|
||||
|
||||
if c < 0 {
|
||||
c = -1
|
||||
} else {
|
||||
c = 1
|
||||
}
|
||||
a := (c + 1) / 2
|
||||
if tree.put(key, value, q, &q.children[a]) {
|
||||
return putFix(int8(c), qp)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (tree *AVLTree) remove(key interface{}, qp **AVLTreeNode) (value interface{}, fix bool) {
|
||||
q := *qp
|
||||
if q == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
c := tree.comparator(key, q.Key)
|
||||
if c == 0 {
|
||||
tree.size--
|
||||
value = q.Value
|
||||
fix = true
|
||||
if q.children[1] == nil {
|
||||
if q.children[0] != nil {
|
||||
q.children[0].parent = q.parent
|
||||
}
|
||||
*qp = q.children[0]
|
||||
return
|
||||
}
|
||||
if removeMin(&q.children[1], &q.Key, &q.Value) {
|
||||
return value, removeFix(-1, qp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if c < 0 {
|
||||
c = -1
|
||||
} else {
|
||||
c = 1
|
||||
}
|
||||
a := (c + 1) / 2
|
||||
value, fix = tree.remove(key, &q.children[a])
|
||||
if fix {
|
||||
return value, removeFix(int8(-c), qp)
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func removeMin(qp **AVLTreeNode, minKey *interface{}, minVal *interface{}) bool {
|
||||
q := *qp
|
||||
if q.children[0] == nil {
|
||||
*minKey = q.Key
|
||||
*minVal = q.Value
|
||||
if q.children[1] != nil {
|
||||
q.children[1].parent = q.parent
|
||||
}
|
||||
*qp = q.children[1]
|
||||
return true
|
||||
}
|
||||
fix := removeMin(&q.children[0], minKey, minVal)
|
||||
if fix {
|
||||
return removeFix(1, qp)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func putFix(c int8, t **AVLTreeNode) bool {
|
||||
s := *t
|
||||
if s.b == 0 {
|
||||
s.b = c
|
||||
return true
|
||||
}
|
||||
|
||||
if s.b == -c {
|
||||
s.b = 0
|
||||
return false
|
||||
}
|
||||
|
||||
if s.children[(c+1)/2].b == c {
|
||||
s = singleRotate(c, s)
|
||||
} else {
|
||||
s = doubleRotate(c, s)
|
||||
}
|
||||
*t = s
|
||||
return false
|
||||
}
|
||||
|
||||
func removeFix(c int8, t **AVLTreeNode) bool {
|
||||
s := *t
|
||||
if s.b == 0 {
|
||||
s.b = c
|
||||
return false
|
||||
}
|
||||
|
||||
if s.b == -c {
|
||||
s.b = 0
|
||||
return true
|
||||
}
|
||||
|
||||
a := (c + 1) / 2
|
||||
if s.children[a].b == 0 {
|
||||
s = rotate(c, s)
|
||||
s.b = -c
|
||||
*t = s
|
||||
return false
|
||||
}
|
||||
|
||||
if s.children[a].b == c {
|
||||
s = singleRotate(c, s)
|
||||
} else {
|
||||
s = doubleRotate(c, s)
|
||||
}
|
||||
*t = s
|
||||
return true
|
||||
}
|
||||
|
||||
func singleRotate(c int8, s *AVLTreeNode) *AVLTreeNode {
|
||||
s.b = 0
|
||||
s = rotate(c, s)
|
||||
s.b = 0
|
||||
return s
|
||||
}
|
||||
|
||||
func doubleRotate(c int8, s *AVLTreeNode) *AVLTreeNode {
|
||||
a := (c + 1) / 2
|
||||
r := s.children[a]
|
||||
s.children[a] = rotate(-c, s.children[a])
|
||||
p := rotate(c, s)
|
||||
|
||||
switch {
|
||||
default:
|
||||
s.b = 0
|
||||
r.b = 0
|
||||
case p.b == c:
|
||||
s.b = -c
|
||||
r.b = 0
|
||||
case p.b == -c:
|
||||
s.b = 0
|
||||
r.b = c
|
||||
}
|
||||
|
||||
p.b = 0
|
||||
return p
|
||||
}
|
||||
|
||||
func rotate(c int8, s *AVLTreeNode) *AVLTreeNode {
|
||||
a := (c + 1) / 2
|
||||
r := s.children[a]
|
||||
s.children[a] = r.children[a^1]
|
||||
if s.children[a] != nil {
|
||||
s.children[a].parent = s
|
||||
}
|
||||
r.children[a^1] = s
|
||||
r.parent = s.parent
|
||||
s.parent = r
|
||||
return r
|
||||
}
|
||||
|
||||
func (tree *AVLTree) bottom(d int) *AVLTreeNode {
|
||||
n := tree.root
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for c := n.children[d]; c != nil; c = n.children[d] {
|
||||
n = c
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Prev returns the previous element in an inorder
|
||||
// walk of the AVL tree.
|
||||
func (node *AVLTreeNode) Prev() *AVLTreeNode {
|
||||
return node.walk1(0)
|
||||
}
|
||||
|
||||
// Next returns the next element in an inorder
|
||||
// walk of the AVL tree.
|
||||
func (node *AVLTreeNode) Next() *AVLTreeNode {
|
||||
return node.walk1(1)
|
||||
}
|
||||
|
||||
func (node *AVLTreeNode) walk1(a int) *AVLTreeNode {
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
n := node
|
||||
if n.children[a] != nil {
|
||||
n = n.children[a]
|
||||
for n.children[a^1] != nil {
|
||||
n = n.children[a^1]
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
p := n.parent
|
||||
for p != nil && p.children[a] == n {
|
||||
n = p
|
||||
p = p.parent
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func output(node *AVLTreeNode, prefix string, isTail bool, str *string) {
|
||||
if node.children[1] != nil {
|
||||
newPrefix := prefix
|
||||
if isTail {
|
||||
newPrefix += "│ "
|
||||
} else {
|
||||
newPrefix += " "
|
||||
}
|
||||
output(node.children[1], newPrefix, false, str)
|
||||
}
|
||||
*str += prefix
|
||||
if isTail {
|
||||
*str += "└── "
|
||||
} else {
|
||||
*str += "┌── "
|
||||
}
|
||||
*str += fmt.Sprintf("%v\n", node.Key)
|
||||
if node.children[0] != nil {
|
||||
newPrefix := prefix
|
||||
if isTail {
|
||||
newPrefix += " "
|
||||
} else {
|
||||
newPrefix += "│ "
|
||||
}
|
||||
output(node.children[0], newPrefix, true, str)
|
||||
}
|
||||
}
|
||||
844
g/container/gtree/gtree_btree.go
Normal file
844
g/container/gtree/gtree_btree.go
Normal file
@ -0,0 +1,844 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). 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 gtree
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BTree holds elements of the B-tree.
|
||||
type BTree struct {
|
||||
mu *rwmutex.RWMutex
|
||||
root *BTreeNode
|
||||
comparator func(v1, v2 interface{}) int
|
||||
size int // Total number of keys in the tree
|
||||
m int // order (maximum number of children)
|
||||
}
|
||||
|
||||
// BTreeNode is a single element within the tree.
|
||||
type BTreeNode struct {
|
||||
Parent *BTreeNode
|
||||
Entries []*BTreeEntry // Contained keys in node
|
||||
Children []*BTreeNode // Children nodes
|
||||
}
|
||||
|
||||
// BTreeEntry represents the key-value pair contained within nodes.
|
||||
type BTreeEntry struct {
|
||||
Key interface{}
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// NewBTree instantiates a B-tree with <m> (maximum number of children) and a custom key comparator.
|
||||
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
// Note that the <m> must be greater or equal than 3, or else it panics.
|
||||
func NewBTree(m int, comparator func(v1, v2 interface{}) int, unsafe...bool) *BTree {
|
||||
if m < 3 {
|
||||
panic("Invalid order, should be at least 3")
|
||||
}
|
||||
return &BTree{
|
||||
comparator : comparator,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
m : m,
|
||||
}
|
||||
}
|
||||
|
||||
// NewBTreeFrom instantiates a B-tree with <m> (maximum number of children), a custom key comparator and data map.
|
||||
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewBTreeFrom(m int, comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe...bool) *BTree {
|
||||
tree := NewBTree(m, comparator, unsafe...)
|
||||
for k, v := range data {
|
||||
tree.doSet(k, v)
|
||||
}
|
||||
return tree
|
||||
}
|
||||
|
||||
// Clone returns a new tree with a copy of current tree.
|
||||
func (tree *BTree) Clone(unsafe ...bool) *BTree {
|
||||
newTree := NewBTree(tree.m, tree.comparator, !tree.mu.IsSafe())
|
||||
newTree.Sets(tree.Map())
|
||||
return newTree
|
||||
}
|
||||
|
||||
// Set inserts key-value item into the tree.
|
||||
func (tree *BTree) Set(key interface{}, value interface{}) {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
tree.doSet(key, value)
|
||||
}
|
||||
|
||||
// doSet inserts key-value pair node into the tree.
|
||||
// If key already exists, then its value is updated with the new value.
|
||||
func (tree *BTree) doSet(key interface{}, value interface{}) {
|
||||
entry := &BTreeEntry{Key: key, Value: value}
|
||||
if tree.root == nil {
|
||||
tree.root = &BTreeNode{Entries: []*BTreeEntry{entry}, Children: []*BTreeNode{}}
|
||||
tree.size++
|
||||
return
|
||||
}
|
||||
|
||||
if tree.insert(tree.root, entry) {
|
||||
tree.size++
|
||||
}
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the tree.
|
||||
func (tree *BTree) Sets(data map[interface{}]interface{}) {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
for k, v := range data {
|
||||
tree.doSet(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Get searches the node in the tree by <key> and returns its value or nil if key is not found in tree.
|
||||
func (tree *BTree) Get(key interface{}) (value interface{}) {
|
||||
value, _ = tree.Search(key)
|
||||
return
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the hash map,
|
||||
// and its return value will be set to the map with <key>.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (tree *BTree) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
if entry := tree.doSearch(key); entry != nil {
|
||||
return entry.Value
|
||||
}
|
||||
if f, ok := value.(func() interface {}); ok {
|
||||
value = f()
|
||||
}
|
||||
tree.doSet(key, value)
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (tree *BTree) GetOrSet(key interface{}, value interface{}) interface{} {
|
||||
if v, ok := tree.Search(key); !ok {
|
||||
return tree.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (tree *BTree) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
|
||||
if v, ok := tree.Search(key); !ok {
|
||||
return tree.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (tree *BTree) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
|
||||
if v, ok := tree.Search(key); !ok {
|
||||
return tree.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with the value by given <key>.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (tree *BTree) GetVar(key interface{}) *gvar.Var {
|
||||
return gvar.New(tree.Get(key), true)
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (tree *BTree) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
|
||||
return gvar.New(tree.GetOrSet(key, value), true)
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (tree *BTree) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(tree.GetOrSetFunc(key, f), true)
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (tree *BTree) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(tree.GetOrSetFuncLock(key, f), true)
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (tree *BTree) SetIfNotExist(key interface{}, value interface{}) bool {
|
||||
if !tree.Contains(key) {
|
||||
tree.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (tree *BTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
|
||||
if !tree.Contains(key) {
|
||||
tree.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (tree *BTree) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
|
||||
if !tree.Contains(key) {
|
||||
tree.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Contains checks whether <key> exists in the tree.
|
||||
func (tree *BTree) Contains(key interface{}) bool {
|
||||
_, ok := tree.Search(key)
|
||||
return ok
|
||||
}
|
||||
|
||||
// Remove remove the node from the tree by key.
|
||||
// Key should adhere to the comparator's type assertion, otherwise method panics.
|
||||
func (tree *BTree) doRemove(key interface{}) (value interface{}) {
|
||||
node, index, found := tree.searchRecursively(tree.root, key)
|
||||
if found {
|
||||
value = node.Entries[index].Value
|
||||
tree.delete(node, index)
|
||||
tree.size--
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Remove removes the node from the tree by <key>.
|
||||
func (tree *BTree) Remove(key interface{}) (value interface{}) {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
return tree.doRemove(key)
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the tree by <keys>.
|
||||
func (tree *BTree) Removes(keys []interface{}) {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
for _, key := range keys {
|
||||
tree.doRemove(key)
|
||||
}
|
||||
}
|
||||
|
||||
// Empty returns true if tree does not contain any nodes
|
||||
func (tree *BTree) IsEmpty() bool {
|
||||
return tree.Size() == 0
|
||||
}
|
||||
|
||||
// Size returns number of nodes in the tree.
|
||||
func (tree *BTree) Size() int {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
return tree.size
|
||||
}
|
||||
|
||||
// Keys returns all keys in asc order.
|
||||
func (tree *BTree) Keys() []interface{} {
|
||||
keys := make([]interface{}, tree.Size())
|
||||
index := 0
|
||||
tree.IteratorAsc(func(key, value interface{}) bool {
|
||||
keys[index] = key
|
||||
index++
|
||||
return true
|
||||
})
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values in asc order based on the key.
|
||||
func (tree *BTree) Values() []interface{} {
|
||||
values := make([]interface{}, tree.Size())
|
||||
index := 0
|
||||
tree.IteratorAsc(func(key, value interface{}) bool {
|
||||
values[index] = value
|
||||
index++
|
||||
return true
|
||||
})
|
||||
return values
|
||||
}
|
||||
|
||||
// Map returns all key-value items as map.
|
||||
func (tree *BTree) Map() map[interface{}]interface{} {
|
||||
m := make(map[interface{}]interface{}, tree.Size())
|
||||
tree.IteratorAsc(func(key, value interface{}) bool {
|
||||
m[key] = value
|
||||
return true
|
||||
})
|
||||
return m
|
||||
}
|
||||
|
||||
// Clear removes all nodes from the tree.
|
||||
func (tree *BTree) Clear() {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
tree.root = nil
|
||||
tree.size = 0
|
||||
}
|
||||
|
||||
// Height returns the height of the tree.
|
||||
func (tree *BTree) Height() int {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
return tree.root.height()
|
||||
}
|
||||
|
||||
// Left returns the left-most (min) entry or nil if tree is empty.
|
||||
func (tree *BTree) Left() *BTreeEntry {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
node := tree.left(tree.root)
|
||||
return node.Entries[0]
|
||||
}
|
||||
|
||||
// Right returns the right-most (max) entry or nil if tree is empty.
|
||||
func (tree *BTree) Right() *BTreeEntry {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
node := tree.right(tree.root)
|
||||
return node.Entries[len(node.Entries) - 1]
|
||||
}
|
||||
|
||||
// String returns a string representation of container (for debugging purposes)
|
||||
func (tree *BTree) String() string {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
var buffer bytes.Buffer
|
||||
if _, err := buffer.WriteString("BTree\n"); err != nil {
|
||||
}
|
||||
if tree.size != 0 {
|
||||
tree.output(&buffer, tree.root, 0, true)
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// Search searches the tree with given <key>.
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (tree *BTree) Search(key interface{}) (value interface{}, found bool) {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
node, index, found := tree.searchRecursively(tree.root, key)
|
||||
if found {
|
||||
return node.Entries[index].Value, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Search searches the tree with given <key> without mutex.
|
||||
// It returns the entry if found or otherwise nil.
|
||||
func (tree *BTree) doSearch(key interface{}) *BTreeEntry {
|
||||
node, index, found := tree.searchRecursively(tree.root, key)
|
||||
if found {
|
||||
return node.Entries[index]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Print prints the tree to stdout.
|
||||
func (tree *BTree) Print() {
|
||||
fmt.Println(tree.String())
|
||||
}
|
||||
|
||||
// Iterator is alias of IteratorAsc.
|
||||
func (tree *BTree) Iterator(f func (key, value interface{}) bool) {
|
||||
tree.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the tree in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (tree *BTree) IteratorAsc(f func (key, value interface{}) bool) {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
node := tree.left(tree.root)
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
entry := node.Entries[0]
|
||||
loop:
|
||||
if entry == nil {
|
||||
return
|
||||
}
|
||||
if !f(entry.Key, entry.Value) {
|
||||
return
|
||||
}
|
||||
// Find current entry position in current node
|
||||
e, _ := tree.search(node, entry.Key)
|
||||
// Try to go down to the child right of the current entry
|
||||
if e + 1 < len(node.Children) {
|
||||
node = node.Children[e + 1]
|
||||
// Try to go down to the child left of the current node
|
||||
for len(node.Children) > 0 {
|
||||
node = node.Children[0]
|
||||
}
|
||||
// Return the left-most entry
|
||||
entry = node.Entries[0]
|
||||
goto loop
|
||||
}
|
||||
// Above assures that we have reached a leaf node, so return the next entry in current node (if any)
|
||||
if e + 1 < len(node.Entries) {
|
||||
entry = node.Entries[e + 1]
|
||||
goto loop
|
||||
}
|
||||
// Reached leaf node and there are no entries to the right of the current entry, so go up to the parent
|
||||
for node.Parent != nil {
|
||||
node = node.Parent
|
||||
// Find next entry position in current node (note: search returns the first equal or bigger than entry)
|
||||
e, _ := tree.search(node, entry.Key)
|
||||
// Check that there is a next entry position in current node
|
||||
if e < len(node.Entries) {
|
||||
entry = node.Entries[e]
|
||||
goto loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the tree in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (tree *BTree) IteratorDesc(f func (key, value interface{}) bool) {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
node := tree.right(tree.root)
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
entry := node.Entries[len(node.Entries) - 1]
|
||||
loop:
|
||||
if entry == nil {
|
||||
return
|
||||
}
|
||||
if !f(entry.Key, entry.Value) {
|
||||
return
|
||||
}
|
||||
// Find current entry position in current node
|
||||
e, _ := tree.search(node, entry.Key)
|
||||
// Try to go down to the child left of the current entry
|
||||
if e < len(node.Children) {
|
||||
node = node.Children[e]
|
||||
// Try to go down to the child right of the current node
|
||||
for len(node.Children) > 0 {
|
||||
node = node.Children[len(node.Children) - 1]
|
||||
}
|
||||
// Return the right-most entry
|
||||
entry = node.Entries[len(node.Entries) - 1]
|
||||
goto loop
|
||||
}
|
||||
// Above assures that we have reached a leaf node, so return the previous entry in current node (if any)
|
||||
if e - 1 >= 0 {
|
||||
entry = node.Entries[e - 1]
|
||||
goto loop
|
||||
}
|
||||
|
||||
// Reached leaf node and there are no entries to the left of the current entry, so go up to the parent
|
||||
for node.Parent != nil {
|
||||
node = node.Parent
|
||||
// Find previous entry position in current node (note: search returns the first equal or bigger than entry)
|
||||
e, _ := tree.search(node, entry.Key)
|
||||
// Check that there is a previous entry position in current node
|
||||
if e - 1 >= 0 {
|
||||
entry = node.Entries[e - 1]
|
||||
goto loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tree *BTree) output(buffer *bytes.Buffer, node *BTreeNode, level int, isTail bool) {
|
||||
for e := 0; e < len(node.Entries)+1; e++ {
|
||||
if e < len(node.Children) {
|
||||
tree.output(buffer, node.Children[e], level+1, true)
|
||||
}
|
||||
if e < len(node.Entries) {
|
||||
if _, err := buffer.WriteString(strings.Repeat(" ", level)); err != nil {
|
||||
}
|
||||
if _, err := buffer.WriteString(fmt.Sprintf("%v", node.Entries[e].Key) + "\n"); err != nil {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (node *BTreeNode) height() int {
|
||||
h := 0
|
||||
n := node
|
||||
for ; n != nil; n = n.Children[0] {
|
||||
h++
|
||||
if len(n.Children) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (tree *BTree) isLeaf(node *BTreeNode) bool {
|
||||
return len(node.Children) == 0
|
||||
}
|
||||
|
||||
func (tree *BTree) isFull(node *BTreeNode) bool {
|
||||
return len(node.Entries) == tree.maxEntries()
|
||||
}
|
||||
|
||||
func (tree *BTree) shouldSplit(node *BTreeNode) bool {
|
||||
return len(node.Entries) > tree.maxEntries()
|
||||
}
|
||||
|
||||
func (tree *BTree) maxChildren() int {
|
||||
return tree.m
|
||||
}
|
||||
|
||||
func (tree *BTree) minChildren() int {
|
||||
return (tree.m + 1) / 2 // ceil(m/2)
|
||||
}
|
||||
|
||||
func (tree *BTree) maxEntries() int {
|
||||
return tree.maxChildren() - 1
|
||||
}
|
||||
|
||||
func (tree *BTree) minEntries() int {
|
||||
return tree.minChildren() - 1
|
||||
}
|
||||
|
||||
func (tree *BTree) middle() int {
|
||||
// "-1" to favor right nodes to have more keys when splitting
|
||||
return (tree.m - 1) / 2
|
||||
}
|
||||
|
||||
// search searches only within the single node among its entries
|
||||
func (tree *BTree) search(node *BTreeNode, key interface{}) (index int, found bool) {
|
||||
low, mid, high := 0, 0, len(node.Entries) - 1
|
||||
for low <= high {
|
||||
mid = (high + low) / 2
|
||||
compare := tree.comparator(key, node.Entries[mid].Key)
|
||||
switch {
|
||||
case compare > 0: low = mid + 1
|
||||
case compare < 0: high = mid - 1
|
||||
case compare == 0: return mid, true
|
||||
}
|
||||
}
|
||||
return low, false
|
||||
}
|
||||
|
||||
// searchRecursively searches recursively down the tree starting at the startNode
|
||||
func (tree *BTree) searchRecursively(startNode *BTreeNode, key interface{}) (node *BTreeNode, index int, found bool) {
|
||||
if tree.size == 0 {
|
||||
return nil, -1, false
|
||||
}
|
||||
node = startNode
|
||||
for {
|
||||
index, found = tree.search(node, key)
|
||||
if found {
|
||||
return node, index, true
|
||||
}
|
||||
if tree.isLeaf(node) {
|
||||
return nil, -1, false
|
||||
}
|
||||
node = node.Children[index]
|
||||
}
|
||||
}
|
||||
|
||||
func (tree *BTree) insert(node *BTreeNode, entry *BTreeEntry) (inserted bool) {
|
||||
if tree.isLeaf(node) {
|
||||
return tree.insertIntoLeaf(node, entry)
|
||||
}
|
||||
return tree.insertIntoInternal(node, entry)
|
||||
}
|
||||
|
||||
func (tree *BTree) insertIntoLeaf(node *BTreeNode, entry *BTreeEntry) (inserted bool) {
|
||||
insertPosition, found := tree.search(node, entry.Key)
|
||||
if found {
|
||||
node.Entries[insertPosition] = entry
|
||||
return false
|
||||
}
|
||||
// Insert entry's key in the middle of the node
|
||||
node.Entries = append(node.Entries, nil)
|
||||
copy(node.Entries[insertPosition+1:], node.Entries[insertPosition:])
|
||||
node.Entries[insertPosition] = entry
|
||||
tree.split(node)
|
||||
return true
|
||||
}
|
||||
|
||||
func (tree *BTree) insertIntoInternal(node *BTreeNode, entry *BTreeEntry) (inserted bool) {
|
||||
insertPosition, found := tree.search(node, entry.Key)
|
||||
if found {
|
||||
node.Entries[insertPosition] = entry
|
||||
return false
|
||||
}
|
||||
return tree.insert(node.Children[insertPosition], entry)
|
||||
}
|
||||
|
||||
func (tree *BTree) split(node *BTreeNode) {
|
||||
if !tree.shouldSplit(node) {
|
||||
return
|
||||
}
|
||||
|
||||
if node == tree.root {
|
||||
tree.splitRoot()
|
||||
return
|
||||
}
|
||||
|
||||
tree.splitNonRoot(node)
|
||||
}
|
||||
|
||||
func (tree *BTree) splitNonRoot(node *BTreeNode) {
|
||||
middle := tree.middle()
|
||||
parent := node.Parent
|
||||
|
||||
left := &BTreeNode{Entries: append([]*BTreeEntry(nil), node.Entries[:middle]...), Parent: parent}
|
||||
right := &BTreeNode{Entries: append([]*BTreeEntry(nil), node.Entries[middle+1:]...), Parent: parent}
|
||||
|
||||
// Move children from the node to be split into left and right nodes
|
||||
if !tree.isLeaf(node) {
|
||||
left.Children = append([]*BTreeNode(nil), node.Children[:middle+1]...)
|
||||
right.Children = append([]*BTreeNode(nil), node.Children[middle+1:]...)
|
||||
setParent(left.Children, left)
|
||||
setParent(right.Children, right)
|
||||
}
|
||||
|
||||
insertPosition, _ := tree.search(parent, node.Entries[middle].Key)
|
||||
|
||||
// Insert middle key into parent
|
||||
parent.Entries = append(parent.Entries, nil)
|
||||
copy(parent.Entries[insertPosition+1:], parent.Entries[insertPosition:])
|
||||
parent.Entries[insertPosition] = node.Entries[middle]
|
||||
|
||||
// Set child left of inserted key in parent to the created left node
|
||||
parent.Children[insertPosition] = left
|
||||
|
||||
// Set child right of inserted key in parent to the created right node
|
||||
parent.Children = append(parent.Children, nil)
|
||||
copy(parent.Children[insertPosition+2:], parent.Children[insertPosition+1:])
|
||||
parent.Children[insertPosition+1] = right
|
||||
|
||||
tree.split(parent)
|
||||
}
|
||||
|
||||
func (tree *BTree) splitRoot() {
|
||||
middle := tree.middle()
|
||||
left := &BTreeNode{Entries: append([]*BTreeEntry(nil), tree.root.Entries[:middle]...)}
|
||||
right := &BTreeNode{Entries: append([]*BTreeEntry(nil), tree.root.Entries[middle+1:]...)}
|
||||
|
||||
// Move children from the node to be split into left and right nodes
|
||||
if !tree.isLeaf(tree.root) {
|
||||
left.Children = append([]*BTreeNode(nil), tree.root.Children[:middle+1]...)
|
||||
right.Children = append([]*BTreeNode(nil), tree.root.Children[middle+1:]...)
|
||||
setParent(left.Children, left)
|
||||
setParent(right.Children, right)
|
||||
}
|
||||
|
||||
// Root is a node with one entry and two children (left and right)
|
||||
newRoot := &BTreeNode{
|
||||
Entries: []*BTreeEntry{tree.root.Entries[middle]},
|
||||
Children: []*BTreeNode{left, right},
|
||||
}
|
||||
|
||||
left.Parent = newRoot
|
||||
right.Parent = newRoot
|
||||
tree.root = newRoot
|
||||
}
|
||||
|
||||
func setParent(nodes []*BTreeNode, parent *BTreeNode) {
|
||||
for _, node := range nodes {
|
||||
node.Parent = parent
|
||||
}
|
||||
}
|
||||
|
||||
func (tree *BTree) left(node *BTreeNode) *BTreeNode {
|
||||
if tree.size == 0 {
|
||||
return nil
|
||||
}
|
||||
current := node
|
||||
for {
|
||||
if tree.isLeaf(current) {
|
||||
return current
|
||||
}
|
||||
current = current.Children[0]
|
||||
}
|
||||
}
|
||||
|
||||
func (tree *BTree) right(node *BTreeNode) *BTreeNode {
|
||||
if tree.size == 0 {
|
||||
return nil
|
||||
}
|
||||
current := node
|
||||
for {
|
||||
if tree.isLeaf(current) {
|
||||
return current
|
||||
}
|
||||
current = current.Children[len(current.Children)-1]
|
||||
}
|
||||
}
|
||||
|
||||
// leftSibling returns the node's left sibling and child index (in parent) if it exists, otherwise (nil,-1)
|
||||
// key is any of keys in node (could even be deleted).
|
||||
func (tree *BTree) leftSibling(node *BTreeNode, key interface{}) (*BTreeNode, int) {
|
||||
if node.Parent != nil {
|
||||
index, _ := tree.search(node.Parent, key)
|
||||
index--
|
||||
if index >= 0 && index < len(node.Parent.Children) {
|
||||
return node.Parent.Children[index], index
|
||||
}
|
||||
}
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
// rightSibling returns the node's right sibling and child index (in parent) if it exists, otherwise (nil,-1)
|
||||
// key is any of keys in node (could even be deleted).
|
||||
func (tree *BTree) rightSibling(node *BTreeNode, key interface{}) (*BTreeNode, int) {
|
||||
if node.Parent != nil {
|
||||
index, _ := tree.search(node.Parent, key)
|
||||
index++
|
||||
if index < len(node.Parent.Children) {
|
||||
return node.Parent.Children[index], index
|
||||
}
|
||||
}
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
// delete deletes an entry in node at entries' index
|
||||
// ref.: https://en.wikipedia.org/wiki/B-tree#Deletion
|
||||
func (tree *BTree) delete(node *BTreeNode, index int) {
|
||||
// deleting from a leaf node
|
||||
if tree.isLeaf(node) {
|
||||
deletedKey := node.Entries[index].Key
|
||||
tree.deleteEntry(node, index)
|
||||
tree.rebalance(node, deletedKey)
|
||||
if len(tree.root.Entries) == 0 {
|
||||
tree.root = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// deleting from an internal node
|
||||
leftLargestNode := tree.right(node.Children[index]) // largest node in the left sub-tree (assumed to exist)
|
||||
leftLargestEntryIndex := len(leftLargestNode.Entries) - 1
|
||||
node.Entries[index] = leftLargestNode.Entries[leftLargestEntryIndex]
|
||||
deletedKey := leftLargestNode.Entries[leftLargestEntryIndex].Key
|
||||
tree.deleteEntry(leftLargestNode, leftLargestEntryIndex)
|
||||
tree.rebalance(leftLargestNode, deletedKey)
|
||||
}
|
||||
|
||||
// rebalance rebalances the tree after deletion if necessary and returns true, otherwise false.
|
||||
// Note that we first delete the entry and then call rebalance, thus the passed deleted key as reference.
|
||||
func (tree *BTree) rebalance(node *BTreeNode, deletedKey interface{}) {
|
||||
// check if rebalancing is needed
|
||||
if node == nil || len(node.Entries) >= tree.minEntries() {
|
||||
return
|
||||
}
|
||||
|
||||
// try to borrow from left sibling
|
||||
leftSibling, leftSiblingIndex := tree.leftSibling(node, deletedKey)
|
||||
if leftSibling != nil && len(leftSibling.Entries) > tree.minEntries() {
|
||||
// rotate right
|
||||
node.Entries = append([]*BTreeEntry{node.Parent.Entries[leftSiblingIndex]}, node.Entries...) // prepend parent's separator entry to node's entries
|
||||
node.Parent.Entries[leftSiblingIndex] = leftSibling.Entries[len(leftSibling.Entries)-1]
|
||||
tree.deleteEntry(leftSibling, len(leftSibling.Entries)-1)
|
||||
if !tree.isLeaf(leftSibling) {
|
||||
leftSiblingRightMostChild := leftSibling.Children[len(leftSibling.Children)-1]
|
||||
leftSiblingRightMostChild.Parent = node
|
||||
node.Children = append([]*BTreeNode{leftSiblingRightMostChild}, node.Children...)
|
||||
tree.deleteChild(leftSibling, len(leftSibling.Children)-1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// try to borrow from right sibling
|
||||
rightSibling, rightSiblingIndex := tree.rightSibling(node, deletedKey)
|
||||
if rightSibling != nil && len(rightSibling.Entries) > tree.minEntries() {
|
||||
// rotate left
|
||||
node.Entries = append(node.Entries, node.Parent.Entries[rightSiblingIndex-1]) // append parent's separator entry to node's entries
|
||||
node.Parent.Entries[rightSiblingIndex-1] = rightSibling.Entries[0]
|
||||
tree.deleteEntry(rightSibling, 0)
|
||||
if !tree.isLeaf(rightSibling) {
|
||||
rightSiblingLeftMostChild := rightSibling.Children[0]
|
||||
rightSiblingLeftMostChild.Parent = node
|
||||
node.Children = append(node.Children, rightSiblingLeftMostChild)
|
||||
tree.deleteChild(rightSibling, 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// merge with siblings
|
||||
if rightSibling != nil {
|
||||
// merge with right sibling
|
||||
node.Entries = append(node.Entries, node.Parent.Entries[rightSiblingIndex-1])
|
||||
node.Entries = append(node.Entries, rightSibling.Entries...)
|
||||
deletedKey = node.Parent.Entries[rightSiblingIndex-1].Key
|
||||
tree.deleteEntry(node.Parent, rightSiblingIndex-1)
|
||||
tree.appendChildren(node.Parent.Children[rightSiblingIndex], node)
|
||||
tree.deleteChild(node.Parent, rightSiblingIndex)
|
||||
} else if leftSibling != nil {
|
||||
// merge with left sibling
|
||||
entries := append([]*BTreeEntry(nil), leftSibling.Entries...)
|
||||
entries = append(entries, node.Parent.Entries[leftSiblingIndex])
|
||||
node.Entries = append(entries, node.Entries...)
|
||||
deletedKey = node.Parent.Entries[leftSiblingIndex].Key
|
||||
tree.deleteEntry(node.Parent, leftSiblingIndex)
|
||||
tree.prependChildren(node.Parent.Children[leftSiblingIndex], node)
|
||||
tree.deleteChild(node.Parent, leftSiblingIndex)
|
||||
}
|
||||
|
||||
// make the merged node the root if its parent was the root and the root is empty
|
||||
if node.Parent == tree.root && len(tree.root.Entries) == 0 {
|
||||
tree.root = node
|
||||
node.Parent = nil
|
||||
return
|
||||
}
|
||||
|
||||
// parent might underflow, so try to rebalance if necessary
|
||||
tree.rebalance(node.Parent, deletedKey)
|
||||
}
|
||||
|
||||
func (tree *BTree) prependChildren(fromNode *BTreeNode, toNode *BTreeNode) {
|
||||
children := append([]*BTreeNode(nil), fromNode.Children...)
|
||||
toNode.Children = append(children, toNode.Children...)
|
||||
setParent(fromNode.Children, toNode)
|
||||
}
|
||||
|
||||
func (tree *BTree) appendChildren(fromNode *BTreeNode, toNode *BTreeNode) {
|
||||
toNode.Children = append(toNode.Children, fromNode.Children...)
|
||||
setParent(fromNode.Children, toNode)
|
||||
}
|
||||
|
||||
func (tree *BTree) deleteEntry(node *BTreeNode, index int) {
|
||||
copy(node.Entries[index:], node.Entries[index+1:])
|
||||
node.Entries[len(node.Entries)-1] = nil
|
||||
node.Entries = node.Entries[:len(node.Entries)-1]
|
||||
}
|
||||
|
||||
func (tree *BTree) deleteChild(node *BTreeNode, index int) {
|
||||
if index >= len(node.Children) {
|
||||
return
|
||||
}
|
||||
copy(node.Children[index:], node.Children[index+1:])
|
||||
node.Children[len(node.Children)-1] = nil
|
||||
node.Children = node.Children[:len(node.Children)-1]
|
||||
}
|
||||
832
g/container/gtree/gtree_redblacktree.go
Normal file
832
g/container/gtree/gtree_redblacktree.go
Normal file
@ -0,0 +1,832 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). 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 gtree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type color bool
|
||||
|
||||
const (
|
||||
black, red color = true, false
|
||||
)
|
||||
|
||||
// RedBlackTree holds elements of the red-black tree.
|
||||
type RedBlackTree struct {
|
||||
mu *rwmutex.RWMutex
|
||||
root *RedBlackTreeNode
|
||||
size int
|
||||
comparator func(v1, v2 interface{}) int
|
||||
}
|
||||
|
||||
// RedBlackTreeNode is a single element within the tree.
|
||||
type RedBlackTreeNode struct {
|
||||
Key interface{}
|
||||
Value interface{}
|
||||
color color
|
||||
left *RedBlackTreeNode
|
||||
right *RedBlackTreeNode
|
||||
parent *RedBlackTreeNode
|
||||
}
|
||||
|
||||
// NewRedBlackTree instantiates a red-black tree with the custom comparator.
|
||||
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewRedBlackTree(comparator func(v1, v2 interface{}) int, unsafe...bool) *RedBlackTree {
|
||||
return &RedBlackTree {
|
||||
mu : rwmutex.New(unsafe...),
|
||||
comparator: comparator,
|
||||
}
|
||||
}
|
||||
|
||||
// NewRedBlackTreeFrom instantiates a red-black tree with the custom comparator and <data> map.
|
||||
// The param <unsafe> used to specify whether using tree in un-concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewRedBlackTreeFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe...bool) *RedBlackTree {
|
||||
tree := NewRedBlackTree(comparator, unsafe...)
|
||||
for k, v := range data {
|
||||
tree.doSet(k, v)
|
||||
}
|
||||
return tree
|
||||
}
|
||||
|
||||
// Clone returns a new tree with a copy of current tree.
|
||||
func (tree *RedBlackTree) Clone(unsafe ...bool) *RedBlackTree {
|
||||
newTree := NewRedBlackTree(tree.comparator, !tree.mu.IsSafe())
|
||||
newTree.Sets(tree.Map())
|
||||
return newTree
|
||||
}
|
||||
|
||||
// Set inserts key-value item into the tree.
|
||||
func (tree *RedBlackTree) Set(key interface{}, value interface{}) {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
tree.doSet(key, value)
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the tree.
|
||||
func (tree *RedBlackTree) Sets(data map[interface{}]interface{}) {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
for k, v := range data {
|
||||
tree.doSet(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
// doSet inserts key-value item into the tree without mutex.
|
||||
func (tree *RedBlackTree) doSet(key interface{}, value interface{}) {
|
||||
insertedNode := (*RedBlackTreeNode)(nil)
|
||||
if tree.root == nil {
|
||||
// Assert key is of comparator's type for initial tree
|
||||
tree.comparator(key, key)
|
||||
tree.root = &RedBlackTreeNode{Key: key, Value: value, color: red}
|
||||
insertedNode = tree.root
|
||||
} else {
|
||||
node := tree.root
|
||||
loop := true
|
||||
for loop {
|
||||
compare := tree.comparator(key, node.Key)
|
||||
switch {
|
||||
case compare == 0:
|
||||
//node.Key = key
|
||||
node.Value = value
|
||||
return
|
||||
case compare < 0:
|
||||
if node.left == nil {
|
||||
node.left = &RedBlackTreeNode{Key: key, Value: value, color: red}
|
||||
insertedNode = node.left
|
||||
loop = false
|
||||
} else {
|
||||
node = node.left
|
||||
}
|
||||
case compare > 0:
|
||||
if node.right == nil {
|
||||
node.right = &RedBlackTreeNode{Key: key, Value: value, color: red}
|
||||
insertedNode = node.right
|
||||
loop = false
|
||||
} else {
|
||||
node = node.right
|
||||
}
|
||||
}
|
||||
}
|
||||
insertedNode.parent = node
|
||||
}
|
||||
tree.insertCase1(insertedNode)
|
||||
tree.size++
|
||||
}
|
||||
|
||||
// Get searches the node in the tree by <key> and returns its value or nil if key is not found in tree.
|
||||
func (tree *RedBlackTree) Get(key interface{}) (value interface{}) {
|
||||
value, _ = tree.Search(key)
|
||||
return
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
//
|
||||
// When setting value, if <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the hash map,
|
||||
// and its return value will be set to the map with <key>.
|
||||
//
|
||||
// It returns value with given <key>.
|
||||
func (tree *RedBlackTree) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
if node := tree.doSearch(key); node != nil {
|
||||
return node.Value
|
||||
}
|
||||
if f, ok := value.(func() interface {}); ok {
|
||||
value = f()
|
||||
}
|
||||
tree.doSet(key, value)
|
||||
return value
|
||||
}
|
||||
|
||||
// GetOrSet returns the value by key,
|
||||
// or set value with given <value> if not exist and returns this value.
|
||||
func (tree *RedBlackTree) GetOrSet(key interface{}, value interface{}) interface{} {
|
||||
if v, ok := tree.Search(key); !ok {
|
||||
return tree.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFunc returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
func (tree *RedBlackTree) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
|
||||
if v, ok := tree.Search(key); !ok {
|
||||
return tree.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrSetFuncLock returns the value by key,
|
||||
// or sets value with return value of callback function <f> if not exist
|
||||
// and returns this value.
|
||||
//
|
||||
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
|
||||
// with mutex.Lock of the hash map.
|
||||
func (tree *RedBlackTree) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
|
||||
if v, ok := tree.Search(key); !ok {
|
||||
return tree.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with the value by given <key>.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (tree *RedBlackTree) GetVar(key interface{}) *gvar.Var {
|
||||
return gvar.New(tree.Get(key), true)
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (tree *RedBlackTree) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
|
||||
return gvar.New(tree.GetOrSet(key, value), true)
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (tree *RedBlackTree) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(tree.GetOrSetFunc(key, f), true)
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
func (tree *RedBlackTree) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(tree.GetOrSetFuncLock(key, f), true)
|
||||
}
|
||||
|
||||
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (tree *RedBlackTree) SetIfNotExist(key interface{}, value interface{}) bool {
|
||||
if !tree.Contains(key) {
|
||||
tree.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
func (tree *RedBlackTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
|
||||
if !tree.Contains(key) {
|
||||
tree.doSetWithLockCheck(key, f())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
|
||||
// It returns false if <key> exists, and <value> would be ignored.
|
||||
//
|
||||
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
|
||||
// it executes function <f> with mutex.Lock of the hash map.
|
||||
func (tree *RedBlackTree) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool {
|
||||
if !tree.Contains(key) {
|
||||
tree.doSetWithLockCheck(key, f)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Contains checks whether <key> exists in the tree.
|
||||
func (tree *RedBlackTree) Contains(key interface{}) bool {
|
||||
_, ok := tree.Search(key)
|
||||
return ok
|
||||
}
|
||||
|
||||
// doRemove removes the node from the tree by <key> without mutex.
|
||||
func (tree *RedBlackTree) doRemove(key interface{}) (value interface{}) {
|
||||
child := (*RedBlackTreeNode)(nil)
|
||||
node := tree.doSearch(key)
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
value = node.Value
|
||||
if node.left != nil && node.right != nil {
|
||||
p := node.left.maximumNode()
|
||||
node.Key = p.Key
|
||||
node.Value = p.Value
|
||||
node = p
|
||||
}
|
||||
if node.left == nil || node.right == nil {
|
||||
if node.right == nil {
|
||||
child = node.left
|
||||
} else {
|
||||
child = node.right
|
||||
}
|
||||
if node.color == black {
|
||||
node.color = tree.nodeColor(child)
|
||||
tree.deleteCase1(node)
|
||||
}
|
||||
tree.replaceNode(node, child)
|
||||
if node.parent == nil && child != nil {
|
||||
child.color = black
|
||||
}
|
||||
}
|
||||
tree.size--
|
||||
return
|
||||
}
|
||||
|
||||
// Remove removes the node from the tree by <key>.
|
||||
func (tree *RedBlackTree) Remove(key interface{}) (value interface{}) {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
return tree.doRemove(key)
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the tree by <keys>.
|
||||
func (tree *RedBlackTree) Removes(keys []interface{}) {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
for _, key := range keys {
|
||||
tree.doRemove(key)
|
||||
}
|
||||
}
|
||||
|
||||
// IsEmpty returns true if tree does not contain any nodes.
|
||||
func (tree *RedBlackTree) IsEmpty() bool {
|
||||
return tree.Size() == 0
|
||||
}
|
||||
|
||||
// Size returns number of nodes in the tree.
|
||||
func (tree *RedBlackTree) Size() int {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
return tree.size
|
||||
}
|
||||
|
||||
// Keys returns all keys in asc order.
|
||||
func (tree *RedBlackTree) Keys() []interface{} {
|
||||
keys := make([]interface{}, tree.Size())
|
||||
index := 0
|
||||
tree.IteratorAsc(func(key, value interface{}) bool {
|
||||
keys[index] = key
|
||||
index++
|
||||
return true
|
||||
})
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values in asc order based on the key.
|
||||
func (tree *RedBlackTree) Values() []interface{} {
|
||||
values := make([]interface{}, tree.Size())
|
||||
index := 0
|
||||
tree.IteratorAsc(func(key, value interface{}) bool {
|
||||
values[index] = value
|
||||
index++
|
||||
return true
|
||||
})
|
||||
return values
|
||||
}
|
||||
|
||||
// Map returns all key-value items as map.
|
||||
func (tree *RedBlackTree) Map() map[interface{}]interface{} {
|
||||
m := make(map[interface{}]interface{}, tree.Size())
|
||||
tree.IteratorAsc(func(key, value interface{}) bool {
|
||||
m[key] = value
|
||||
return true
|
||||
})
|
||||
return m
|
||||
}
|
||||
|
||||
// Left returns the left-most (min) node or nil if tree is empty.
|
||||
func (tree *RedBlackTree) Left() *RedBlackTreeNode {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
node := tree.leftNode()
|
||||
if tree.mu.IsSafe() {
|
||||
return &RedBlackTreeNode{
|
||||
Key : node.Key,
|
||||
Value : node.Value,
|
||||
}
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
// Right returns the right-most (max) node or nil if tree is empty.
|
||||
func (tree *RedBlackTree) Right() *RedBlackTreeNode {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
node := tree.rightNode()
|
||||
if tree.mu.IsSafe() {
|
||||
return &RedBlackTreeNode{
|
||||
Key : node.Key,
|
||||
Value : node.Value,
|
||||
}
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
// leftNode returns the left-most (min) node or nil if tree is empty.
|
||||
func (tree *RedBlackTree) leftNode() *RedBlackTreeNode {
|
||||
p := (*RedBlackTreeNode)(nil)
|
||||
n := tree.root
|
||||
for n != nil {
|
||||
p = n
|
||||
n = n.left
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// rightNode returns the right-most (max) node or nil if tree is empty.
|
||||
func (tree *RedBlackTree) rightNode() *RedBlackTreeNode {
|
||||
p := (*RedBlackTreeNode)(nil)
|
||||
n := tree.root
|
||||
for n != nil {
|
||||
p = n
|
||||
n = n.right
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Floor Finds floor node of the input <key>, return the floor node or nil if no floor is found.
|
||||
//
|
||||
// Floor node is defined as the largest node that its key is smaller than or equal to the given <key>.
|
||||
// A floor node may not be found, either because the tree is empty, or because
|
||||
// all nodes in the tree are larger than the given node.
|
||||
func (tree *RedBlackTree) Floor(key interface{}) (floor *RedBlackTreeNode) {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
found := false
|
||||
node := tree.root
|
||||
for node != nil {
|
||||
compare := tree.comparator(key, node.Key)
|
||||
switch {
|
||||
case compare == 0:
|
||||
return node
|
||||
case compare < 0:
|
||||
node = node.left
|
||||
case compare > 0:
|
||||
floor, found = node, true
|
||||
node = node.right
|
||||
}
|
||||
}
|
||||
if found {
|
||||
return floor
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ceiling finds ceiling node of the input <key>, return the ceiling node or nil if no ceiling is found.
|
||||
//
|
||||
// Ceiling node is defined as the smallest node that its key is larger than or equal to the given <key>.
|
||||
// A ceiling node may not be found, either because the tree is empty, or because
|
||||
// all nodes in the tree are smaller than the given node.
|
||||
func (tree *RedBlackTree) Ceiling(key interface{}) (ceiling *RedBlackTreeNode) {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
found := false
|
||||
node := tree.root
|
||||
for node != nil {
|
||||
compare := tree.comparator(key, node.Key)
|
||||
switch {
|
||||
case compare == 0:
|
||||
return node
|
||||
case compare < 0:
|
||||
ceiling, found = node, true
|
||||
node = node.left
|
||||
case compare > 0:
|
||||
node = node.right
|
||||
}
|
||||
}
|
||||
if found {
|
||||
return ceiling
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Iterator is alias of IteratorAsc.
|
||||
func (tree *RedBlackTree) Iterator(f func (key, value interface{}) bool) {
|
||||
tree.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the tree in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (tree *RedBlackTree) IteratorAsc(f func (key, value interface{}) bool) {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
node := tree.leftNode()
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
loop:
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
if !f(node.Key, node.Value) {
|
||||
return
|
||||
}
|
||||
if node.right != nil {
|
||||
node = node.right
|
||||
for node.left != nil {
|
||||
node = node.left
|
||||
}
|
||||
goto loop
|
||||
}
|
||||
if node.parent != nil {
|
||||
old := node
|
||||
for node.parent != nil {
|
||||
node = node.parent
|
||||
if tree.comparator(old.Key, node.Key) <= 0 {
|
||||
goto loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the tree in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (tree *RedBlackTree) IteratorDesc(f func (key, value interface{}) bool) {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
node := tree.rightNode()
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
loop:
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
if !f(node.Key, node.Value) {
|
||||
return
|
||||
}
|
||||
if node.left != nil {
|
||||
node = node.left
|
||||
for node.right != nil {
|
||||
node = node.right
|
||||
}
|
||||
goto loop
|
||||
}
|
||||
if node.parent != nil {
|
||||
old := node
|
||||
for node.parent != nil {
|
||||
node = node.parent
|
||||
if tree.comparator(old.Key, node.Key) >= 0 {
|
||||
goto loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear removes all nodes from the tree.
|
||||
func (tree *RedBlackTree) Clear() {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
tree.root = nil
|
||||
tree.size = 0
|
||||
}
|
||||
|
||||
// String returns a string representation of container.
|
||||
func (tree *RedBlackTree) String() string {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
str := "RedBlackTree\n"
|
||||
if tree.size != 0 {
|
||||
tree.output(tree.root, "", true, &str)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// Print prints the tree to stdout.
|
||||
func (tree *RedBlackTree) Print() {
|
||||
fmt.Println(tree.String())
|
||||
}
|
||||
|
||||
// Search searches the tree with given <key>.
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (tree *RedBlackTree) Search(key interface{}) (value interface{}, found bool) {
|
||||
tree.mu.RLock()
|
||||
defer tree.mu.RUnlock()
|
||||
node := tree.doSearch(key)
|
||||
if node != nil {
|
||||
return node.Value, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Flip exchanges key-value of the tree to value-key.
|
||||
// Note that you should guarantee the value is the same type as key,
|
||||
// or else the comparator would panic.
|
||||
//
|
||||
// If the type of value is different with key, you pass the new <comparator>.
|
||||
func (tree *RedBlackTree) Flip(comparator...func(v1, v2 interface{}) int) {
|
||||
t := (*RedBlackTree)(nil)
|
||||
if len(comparator) > 0 {
|
||||
t = NewRedBlackTree(comparator[0], !tree.mu.IsSafe())
|
||||
} else {
|
||||
t = NewRedBlackTree(tree.comparator, !tree.mu.IsSafe())
|
||||
}
|
||||
tree.IteratorAsc(func(key, value interface{}) bool {
|
||||
t.doSet(value, key)
|
||||
return true
|
||||
})
|
||||
tree.mu.Lock()
|
||||
tree.root = t.root
|
||||
tree.size = t.size
|
||||
tree.mu.Unlock()
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) output(node *RedBlackTreeNode, prefix string, isTail bool, str *string) {
|
||||
if node.right != nil {
|
||||
newPrefix := prefix
|
||||
if isTail {
|
||||
newPrefix += "│ "
|
||||
} else {
|
||||
newPrefix += " "
|
||||
}
|
||||
tree.output(node.right, newPrefix, false, str)
|
||||
}
|
||||
*str += prefix
|
||||
if isTail {
|
||||
*str += "└── "
|
||||
} else {
|
||||
*str += "┌── "
|
||||
}
|
||||
*str += fmt.Sprintf("%v\n", node.Key)
|
||||
if node.left != nil {
|
||||
newPrefix := prefix
|
||||
if isTail {
|
||||
newPrefix += " "
|
||||
} else {
|
||||
newPrefix += "│ "
|
||||
}
|
||||
tree.output(node.left, newPrefix, true, str)
|
||||
}
|
||||
}
|
||||
|
||||
// doSearch searches the tree with given <key> without mutex.
|
||||
// It returns the node if found or otherwise nil.
|
||||
func (tree *RedBlackTree) doSearch(key interface{}) *RedBlackTreeNode {
|
||||
node := tree.root
|
||||
for node != nil {
|
||||
compare := tree.comparator(key, node.Key)
|
||||
switch {
|
||||
case compare == 0: return node
|
||||
case compare < 0: node = node.left
|
||||
case compare > 0: node = node.right
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (node *RedBlackTreeNode) grandparent() *RedBlackTreeNode {
|
||||
if node != nil && node.parent != nil {
|
||||
return node.parent.parent
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (node *RedBlackTreeNode) uncle() *RedBlackTreeNode {
|
||||
if node == nil || node.parent == nil || node.parent.parent == nil {
|
||||
return nil
|
||||
}
|
||||
return node.parent.sibling()
|
||||
}
|
||||
|
||||
func (node *RedBlackTreeNode) sibling() *RedBlackTreeNode {
|
||||
if node == nil || node.parent == nil {
|
||||
return nil
|
||||
}
|
||||
if node == node.parent.left {
|
||||
return node.parent.right
|
||||
}
|
||||
return node.parent.left
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) rotateLeft(node *RedBlackTreeNode) {
|
||||
right := node.right
|
||||
tree.replaceNode(node, right)
|
||||
node.right = right.left
|
||||
if right.left != nil {
|
||||
right.left.parent = node
|
||||
}
|
||||
right.left = node
|
||||
node.parent = right
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) rotateRight(node *RedBlackTreeNode) {
|
||||
left := node.left
|
||||
tree.replaceNode(node, left)
|
||||
node.left = left.right
|
||||
if left.right != nil {
|
||||
left.right.parent = node
|
||||
}
|
||||
left.right = node
|
||||
node.parent = left
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) replaceNode(old *RedBlackTreeNode, new *RedBlackTreeNode) {
|
||||
if old.parent == nil {
|
||||
tree.root = new
|
||||
} else {
|
||||
if old == old.parent.left {
|
||||
old.parent.left = new
|
||||
} else {
|
||||
old.parent.right = new
|
||||
}
|
||||
}
|
||||
if new != nil {
|
||||
new.parent = old.parent
|
||||
}
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) insertCase1(node *RedBlackTreeNode) {
|
||||
if node.parent == nil {
|
||||
node.color = black
|
||||
} else {
|
||||
tree.insertCase2(node)
|
||||
}
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) insertCase2(node *RedBlackTreeNode) {
|
||||
if tree.nodeColor(node.parent) == black {
|
||||
return
|
||||
}
|
||||
tree.insertCase3(node)
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) insertCase3(node *RedBlackTreeNode) {
|
||||
uncle := node.uncle()
|
||||
if tree.nodeColor(uncle) == red {
|
||||
node.parent.color = black
|
||||
uncle.color = black
|
||||
node.grandparent().color = red
|
||||
tree.insertCase1(node.grandparent())
|
||||
} else {
|
||||
tree.insertCase4(node)
|
||||
}
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) insertCase4(node *RedBlackTreeNode) {
|
||||
grandparent := node.grandparent()
|
||||
if node == node.parent.right && node.parent == grandparent.left {
|
||||
tree.rotateLeft(node.parent)
|
||||
node = node.left
|
||||
} else if node == node.parent.left && node.parent == grandparent.right {
|
||||
tree.rotateRight(node.parent)
|
||||
node = node.right
|
||||
}
|
||||
tree.insertCase5(node)
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) insertCase5(node *RedBlackTreeNode) {
|
||||
node.parent.color = black
|
||||
grandparent := node.grandparent()
|
||||
grandparent.color = red
|
||||
if node == node.parent.left && node.parent == grandparent.left {
|
||||
tree.rotateRight(grandparent)
|
||||
} else if node == node.parent.right && node.parent == grandparent.right {
|
||||
tree.rotateLeft(grandparent)
|
||||
}
|
||||
}
|
||||
|
||||
func (node *RedBlackTreeNode) maximumNode() *RedBlackTreeNode {
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
for node.right != nil {
|
||||
return node.right
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) deleteCase1(node *RedBlackTreeNode) {
|
||||
if node.parent == nil {
|
||||
return
|
||||
}
|
||||
tree.deleteCase2(node)
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) deleteCase2(node *RedBlackTreeNode) {
|
||||
sibling := node.sibling()
|
||||
if tree.nodeColor(sibling) == red {
|
||||
node.parent.color = red
|
||||
sibling.color = black
|
||||
if node == node.parent.left {
|
||||
tree.rotateLeft(node.parent)
|
||||
} else {
|
||||
tree.rotateRight(node.parent)
|
||||
}
|
||||
}
|
||||
tree.deleteCase3(node)
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) deleteCase3(node *RedBlackTreeNode) {
|
||||
sibling := node.sibling()
|
||||
if tree.nodeColor(node.parent) == black &&
|
||||
tree.nodeColor(sibling) == black &&
|
||||
tree.nodeColor(sibling.left) == black &&
|
||||
tree.nodeColor(sibling.right) == black {
|
||||
sibling.color = red
|
||||
tree.deleteCase1(node.parent)
|
||||
} else {
|
||||
tree.deleteCase4(node)
|
||||
}
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) deleteCase4(node *RedBlackTreeNode) {
|
||||
sibling := node.sibling()
|
||||
if tree.nodeColor(node.parent) == red &&
|
||||
tree.nodeColor(sibling) == black &&
|
||||
tree.nodeColor(sibling.left) == black &&
|
||||
tree.nodeColor(sibling.right) == black {
|
||||
sibling.color = red
|
||||
node.parent.color = black
|
||||
} else {
|
||||
tree.deleteCase5(node)
|
||||
}
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) deleteCase5(node *RedBlackTreeNode) {
|
||||
sibling := node.sibling()
|
||||
if node == node.parent.left &&
|
||||
tree.nodeColor(sibling) == black &&
|
||||
tree.nodeColor(sibling.left) == red &&
|
||||
tree.nodeColor(sibling.right) == black {
|
||||
sibling.color = red
|
||||
sibling.left.color = black
|
||||
tree.rotateRight(sibling)
|
||||
} else if node == node.parent.right &&
|
||||
tree.nodeColor(sibling) == black &&
|
||||
tree.nodeColor(sibling.right) == red &&
|
||||
tree.nodeColor(sibling.left) == black {
|
||||
sibling.color = red
|
||||
sibling.right.color = black
|
||||
tree.rotateLeft(sibling)
|
||||
}
|
||||
tree.deleteCase6(node)
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) deleteCase6(node *RedBlackTreeNode) {
|
||||
sibling := node.sibling()
|
||||
sibling.color = tree.nodeColor(node.parent)
|
||||
node.parent.color = black
|
||||
if node == node.parent.left && tree.nodeColor(sibling.right) == red {
|
||||
sibling.right.color = black
|
||||
tree.rotateLeft(node.parent)
|
||||
} else if tree.nodeColor(sibling.left) == red {
|
||||
sibling.left.color = black
|
||||
tree.rotateRight(node.parent)
|
||||
}
|
||||
}
|
||||
|
||||
func (tree *RedBlackTree) nodeColor(node *RedBlackTreeNode) color {
|
||||
if node == nil {
|
||||
return black
|
||||
}
|
||||
return node.color
|
||||
}
|
||||
103
g/container/gtree/gtree_z_avl_tree_test.go
Normal file
103
g/container/gtree/gtree_z_avl_tree_test.go
Normal file
@ -0,0 +1,103 @@
|
||||
// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtree_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gtree"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"github.com/gogf/gf/g/util/gutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func Test_AVLTree_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gtree.NewAVLTree(gutil.ComparatorString)
|
||||
m.Set("key1", "val1")
|
||||
gtest.Assert(m.Keys(), []interface{}{"key1"})
|
||||
|
||||
gtest.Assert(m.Get("key1"), "val1")
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("key2", "val2"), "val2")
|
||||
gtest.Assert(m.SetIfNotExist("key2", "val2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("key3", "val3"), true)
|
||||
|
||||
gtest.Assert(m.Remove("key2"), "val2")
|
||||
gtest.Assert(m.Contains("key2"), false)
|
||||
|
||||
gtest.AssertIN("key3", m.Keys())
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
gtest.AssertIN("val3", m.Values())
|
||||
gtest.AssertIN("val1", m.Values())
|
||||
|
||||
m.Flip()
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gtree.NewAVLTreeFrom(gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
})
|
||||
}
|
||||
func Test_AVLTree_Set_Fun(t *testing.T) {
|
||||
m := gtree.NewAVLTree(gutil.ComparatorString)
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
m.GetOrSetFuncLock("funlock", getValue)
|
||||
gtest.Assert(m.Get("funlock"), 3)
|
||||
gtest.Assert(m.Get("fun"), 3)
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
|
||||
}
|
||||
|
||||
func Test_AVLTree_Batch(t *testing.T) {
|
||||
m := gtree.NewAVLTree(gutil.ComparatorString)
|
||||
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
m.Removes([]interface{}{"key1", 1})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
|
||||
}
|
||||
func Test_AVLTree_Iterator(t *testing.T){
|
||||
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
|
||||
|
||||
m := gtree.NewAVLTreeFrom(gutil.ComparatorString, expect)
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, 2)
|
||||
gtest.Assert(j, 1)
|
||||
}
|
||||
|
||||
func Test_AVLTree_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gtree.NewAVLTreeFrom(gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove("key1")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
}
|
||||
99
g/container/gtree/gtree_z_b_tree_test.go
Normal file
99
g/container/gtree/gtree_z_b_tree_test.go
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtree_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gtree"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"github.com/gogf/gf/g/util/gutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_BTree_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gtree.NewBTree(3, gutil.ComparatorString)
|
||||
m.Set("key1", "val1")
|
||||
gtest.Assert(m.Keys(), []interface{}{"key1"})
|
||||
|
||||
gtest.Assert(m.Get("key1"), "val1")
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("key2", "val2"), "val2")
|
||||
gtest.Assert(m.SetIfNotExist("key2", "val2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("key3", "val3"), true)
|
||||
|
||||
gtest.Assert(m.Remove("key2"), "val2")
|
||||
gtest.Assert(m.Contains("key2"), false)
|
||||
|
||||
gtest.AssertIN("key3", m.Keys())
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
gtest.AssertIN("val3", m.Values())
|
||||
gtest.AssertIN("val1", m.Values())
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gtree.NewBTreeFrom(3, gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
})
|
||||
}
|
||||
func Test_BTree_Set_Fun(t *testing.T) {
|
||||
m := gtree.NewBTree(3, gutil.ComparatorString)
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
m.GetOrSetFuncLock("funlock", getValue)
|
||||
gtest.Assert(m.Get("funlock"), 3)
|
||||
gtest.Assert(m.Get("fun"), 3)
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
|
||||
}
|
||||
|
||||
func Test_BTree_Batch(t *testing.T) {
|
||||
m := gtree.NewBTree(3, gutil.ComparatorString)
|
||||
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
m.Removes([]interface{}{"key1", 1})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
|
||||
}
|
||||
func Test_BTree_Iterator(t *testing.T){
|
||||
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
|
||||
|
||||
m := gtree.NewBTreeFrom(3, gutil.ComparatorString, expect)
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, 2)
|
||||
gtest.Assert(j, 1)
|
||||
}
|
||||
|
||||
func Test_BTree_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gtree.NewBTreeFrom(3, gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove("key1")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
}
|
||||
106
g/container/gtree/gtree_z_redblack_tree_test.go
Normal file
106
g/container/gtree/gtree_z_redblack_tree_test.go
Normal file
@ -0,0 +1,106 @@
|
||||
// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). 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 gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtree_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gtree"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"github.com/gogf/gf/g/util/gutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getValue() interface{} {
|
||||
return 3
|
||||
}
|
||||
|
||||
func Test_RedBlackTree_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gtree.NewRedBlackTree(gutil.ComparatorString)
|
||||
m.Set("key1", "val1")
|
||||
gtest.Assert(m.Keys(), []interface{}{"key1"})
|
||||
|
||||
gtest.Assert(m.Get("key1"), "val1")
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("key2", "val2"), "val2")
|
||||
gtest.Assert(m.SetIfNotExist("key2", "val2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("key3", "val3"), true)
|
||||
|
||||
gtest.Assert(m.Remove("key2"), "val2")
|
||||
gtest.Assert(m.Contains("key2"), false)
|
||||
|
||||
gtest.AssertIN("key3", m.Keys())
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
gtest.AssertIN("val3", m.Values())
|
||||
gtest.AssertIN("val1", m.Values())
|
||||
|
||||
m.Flip()
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gtree.NewRedBlackTreeFrom(gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
})
|
||||
}
|
||||
func Test_RedBlackTree_Set_Fun(t *testing.T) {
|
||||
m := gtree.NewRedBlackTree(gutil.ComparatorString)
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
m.GetOrSetFuncLock("funlock", getValue)
|
||||
gtest.Assert(m.Get("funlock"), 3)
|
||||
gtest.Assert(m.Get("fun"), 3)
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
|
||||
}
|
||||
|
||||
func Test_RedBlackTree_Batch(t *testing.T) {
|
||||
m := gtree.NewRedBlackTree(gutil.ComparatorString)
|
||||
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
m.Removes([]interface{}{"key1", 1})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
|
||||
}
|
||||
func Test_RedBlackTree_Iterator(t *testing.T){
|
||||
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
|
||||
|
||||
m := gtree.NewRedBlackTreeFrom(gutil.ComparatorString, expect)
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, 2)
|
||||
gtest.Assert(j, 1)
|
||||
}
|
||||
|
||||
func Test_RedBlackTree_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gtree.NewRedBlackTreeFrom(gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove("key1")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
}
|
||||
@ -33,7 +33,7 @@ func (t *Bool) Clone() *Bool {
|
||||
return NewBool(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.valueue and returns the previous t.value value.
|
||||
// Set atomically stores <value> into t.value and returns the previous value of t.value.
|
||||
func (t *Bool) Set(value bool) (old bool) {
|
||||
if value {
|
||||
old = atomic.SwapInt32(&t.value, 1) == 1
|
||||
|
||||
@ -30,7 +30,7 @@ func (t *Byte) Clone() *Byte {
|
||||
return NewByte(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
// Set atomically stores <value> into t.value and returns the previous value of t.value.
|
||||
func (t *Byte) Set(value byte) (old byte) {
|
||||
return byte(atomic.SwapInt32(&t.value, int32(value)))
|
||||
}
|
||||
@ -40,7 +40,7 @@ func (t *Byte) Val() byte {
|
||||
return byte(atomic.LoadInt32(&t.value))
|
||||
}
|
||||
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
// Add atomically adds <delta> to t.value and returns the new value.
|
||||
func (t *Byte) Add(delta int) (new byte) {
|
||||
return byte(atomic.AddInt32(&t.value, int32(delta)))
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ func (t *Bytes) Clone() *Bytes {
|
||||
return NewBytes(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
// Set atomically stores <value> into t.value and returns the previous value of t.value.
|
||||
// Note: The parameter <value> cannot be nil.
|
||||
func (t *Bytes) Set(value []byte) (old []byte) {
|
||||
old = t.Val()
|
||||
|
||||
@ -32,7 +32,7 @@ func (t *Float32) Clone() *Float32 {
|
||||
return NewFloat32(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
// Set atomically stores <value> into t.value and returns the previous value of t.value.
|
||||
func (t *Float32) Set(value float32) (old float32) {
|
||||
return math.Float32frombits(atomic.SwapUint32(&t.value, math.Float32bits(value)))
|
||||
}
|
||||
@ -42,7 +42,7 @@ func (t *Float32) Val() float32 {
|
||||
return math.Float32frombits(atomic.LoadUint32(&t.value))
|
||||
}
|
||||
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
// Add atomically adds <delta> to t.value and returns the new value.
|
||||
func (t *Float32) Add(delta float32) (new float32) {
|
||||
for {
|
||||
old := math.Float32frombits(t.value)
|
||||
|
||||
@ -32,7 +32,7 @@ func (t *Float64) Clone() *Float64 {
|
||||
return NewFloat64(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
// Set atomically stores <value> into t.value and returns the previous value of t.value.
|
||||
func (t *Float64) Set(value float64) (old float64) {
|
||||
return math.Float64frombits(atomic.SwapUint64(&t.value, math.Float64bits(value)))
|
||||
}
|
||||
@ -42,7 +42,7 @@ func (t *Float64) Val() float64 {
|
||||
return math.Float64frombits(atomic.LoadUint64(&t.value))
|
||||
}
|
||||
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
// Add atomically adds <delta> to t.value and returns the new value.
|
||||
func (t *Float64) Add(delta float64) (new float64) {
|
||||
for {
|
||||
old := math.Float64frombits(t.value)
|
||||
|
||||
@ -30,7 +30,7 @@ func (t *Int) Clone() *Int {
|
||||
return NewInt(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
// Set atomically stores <value> into t.value and returns the previous value of t.value.
|
||||
func (t *Int) Set(value int) (old int) {
|
||||
return int(atomic.SwapInt64(&t.value, int64(value)))
|
||||
}
|
||||
@ -40,7 +40,7 @@ func (t *Int) Val() int {
|
||||
return int(atomic.LoadInt64(&t.value))
|
||||
}
|
||||
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
// Add atomically adds <delta> to t.value and returns the new value.
|
||||
func (t *Int) Add(delta int) (new int) {
|
||||
return int(atomic.AddInt64(&t.value, int64(delta)))
|
||||
}
|
||||
@ -30,7 +30,7 @@ func (t *Int32) Clone() *Int32 {
|
||||
return NewInt32(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
// Set atomically stores <value> into t.value and returns the previous value of t.value.
|
||||
func (t *Int32) Set(value int32) (old int32) {
|
||||
return atomic.SwapInt32(&t.value, value)
|
||||
}
|
||||
@ -40,7 +40,7 @@ func (t *Int32) Val() int32 {
|
||||
return atomic.LoadInt32(&t.value)
|
||||
}
|
||||
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
// Add atomically adds <delta> to t.value and returns the new value.
|
||||
func (t *Int32) Add(delta int32) (new int32) {
|
||||
return atomic.AddInt32(&t.value, delta)
|
||||
}
|
||||
@ -30,7 +30,7 @@ func (t *Int64) Clone() *Int64 {
|
||||
return NewInt64(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
// Set atomically stores <value> into t.value and returns the previous value of t.value.
|
||||
func (t *Int64) Set(value int64) (old int64) {
|
||||
return atomic.SwapInt64(&t.value, value)
|
||||
}
|
||||
@ -40,7 +40,7 @@ func (t *Int64) Val() int64 {
|
||||
return atomic.LoadInt64(&t.value)
|
||||
}
|
||||
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
// Add atomically adds <delta> to t.value and returns the new value.
|
||||
func (t *Int64) Add(delta int64) int64 {
|
||||
return atomic.AddInt64(&t.value, delta)
|
||||
}
|
||||
@ -29,7 +29,7 @@ func (t *Interface) Clone() *Interface {
|
||||
return NewInterface(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
// Set atomically stores <value> into t.value and returns the previous value of t.value.
|
||||
// Note: The parameter <value> cannot be nil.
|
||||
func (t *Interface) Set(value interface{}) (old interface{}) {
|
||||
old = t.Val()
|
||||
|
||||
@ -29,7 +29,7 @@ func (t *String) Clone() *String {
|
||||
return NewString(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
// Set atomically stores <value> into t.value and returns the previous value of t.value.
|
||||
func (t *String) Set(value string) (old string) {
|
||||
old = t.Val()
|
||||
t.value.Store(value)
|
||||
|
||||
@ -30,7 +30,7 @@ func (t *Uint) Clone() *Uint {
|
||||
return NewUint(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
// Set atomically stores <value> into t.value and returns the previous value of t.value.
|
||||
func (t *Uint) Set(value uint) (old uint) {
|
||||
return uint(atomic.SwapUint64(&t.value, uint64(value)))
|
||||
}
|
||||
@ -40,7 +40,7 @@ func (t *Uint) Val() uint {
|
||||
return uint(atomic.LoadUint64(&t.value))
|
||||
}
|
||||
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
// Add atomically adds <delta> to t.value and returns the new value.
|
||||
func (t *Uint) Add(delta uint) (new uint) {
|
||||
return uint(atomic.AddUint64(&t.value, uint64(delta)))
|
||||
}
|
||||
@ -30,7 +30,7 @@ func (t *Uint32) Clone() *Uint32 {
|
||||
return NewUint32(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
// Set atomically stores <value> into t.value and returns the previous value of t.value.
|
||||
func (t *Uint32) Set(value uint32) (old uint32) {
|
||||
return atomic.SwapUint32(&t.value, value)
|
||||
}
|
||||
@ -40,7 +40,7 @@ func (t *Uint32) Val() uint32 {
|
||||
return atomic.LoadUint32(&t.value)
|
||||
}
|
||||
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
// Add atomically adds <delta> to t.value and returns the new value.
|
||||
func (t *Uint32) Add(delta uint32) (new uint32) {
|
||||
return atomic.AddUint32(&t.value, delta)
|
||||
}
|
||||
@ -30,7 +30,7 @@ func (t *Uint64) Clone() *Uint64 {
|
||||
return NewUint64(t.Val())
|
||||
}
|
||||
|
||||
// Set atomically stores value into t.value and returns the previous t.value value.
|
||||
// Set atomically stores <value> into t.value and returns the previous value of t.value.
|
||||
func (t *Uint64) Set(value uint64) (old uint64) {
|
||||
return atomic.SwapUint64(&t.value, value)
|
||||
}
|
||||
@ -40,7 +40,7 @@ func (t *Uint64) Val() uint64 {
|
||||
return atomic.LoadUint64(&t.value)
|
||||
}
|
||||
|
||||
// Add atomically adds delta to t.value and returns the new value.
|
||||
// Add atomically adds <delta> to t.value and returns the new value.
|
||||
func (t *Uint64) Add(delta uint64) (new uint64) {
|
||||
return atomic.AddUint64(&t.value, delta)
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright 2018-2019 gf Author(https://github.com/gogf/gf). 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,
|
||||
@ -15,11 +15,13 @@ import (
|
||||
)
|
||||
|
||||
type Var struct {
|
||||
value interface{} // 变量值
|
||||
safe bool // 当为true时, value为 *gtype.Interface 类型
|
||||
value interface{} // Underlying value.
|
||||
safe bool // Concurrent safe or not.
|
||||
}
|
||||
|
||||
// 创建一个动态变量,value参数可以为nil
|
||||
// New returns a new Var with given <value>.
|
||||
// The param <unsafe> used to specify whether using Var in un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe.
|
||||
func New(value interface{}, unsafe...bool) *Var {
|
||||
v := &Var{}
|
||||
if len(unsafe) == 0 || !unsafe[0] {
|
||||
@ -31,16 +33,7 @@ func New(value interface{}, unsafe...bool) *Var {
|
||||
return v
|
||||
}
|
||||
|
||||
// 创建一个只读动态变量,value参数可以为nil
|
||||
func NewRead(value interface{}, unsafe...bool) VarRead {
|
||||
return VarRead(New(value, unsafe...))
|
||||
}
|
||||
|
||||
// 返回动态变量的只读接口
|
||||
func (v *Var) ReadOnly() VarRead {
|
||||
return VarRead(v)
|
||||
}
|
||||
|
||||
// Set sets <value> to <v>, and returns the old value.
|
||||
func (v *Var) Set(value interface{}) (old interface{}) {
|
||||
if v.safe {
|
||||
old = v.value.(*gtype.Interface).Set(value)
|
||||
@ -51,6 +44,7 @@ func (v *Var) Set(value interface{}) (old interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// Val returns the current value of <v>.
|
||||
func (v *Var) Val() interface{} {
|
||||
if v.safe {
|
||||
return v.value.(*gtype.Interface).Val()
|
||||
@ -59,11 +53,38 @@ func (v *Var) Val() interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// Val() 别名
|
||||
// See Val().
|
||||
func (v *Var) Interface() interface{} {
|
||||
return v.Val()
|
||||
}
|
||||
|
||||
// Time converts and returns <v> as time.Time.
|
||||
// The param <format> specifies the format of the time string using gtime,
|
||||
// eg: Y-m-d H:i:s.
|
||||
func (v *Var) Time(format...string) time.Time {
|
||||
return gconv.Time(v.Val(), format...)
|
||||
}
|
||||
|
||||
// TimeDuration converts and returns <v> as time.Duration.
|
||||
// If value of <v> is string, then it uses time.ParseDuration for conversion.
|
||||
func (v *Var) Duration() time.Duration {
|
||||
return gconv.Duration(v.Val())
|
||||
}
|
||||
|
||||
// GTime converts and returns <v> as *gtime.Time.
|
||||
// The param <format> specifies the format of the time string using gtime,
|
||||
// eg: Y-m-d H:i:s.
|
||||
func (v *Var) GTime(format...string) *gtime.Time {
|
||||
return gconv.GTime(v.Val(), format...)
|
||||
}
|
||||
|
||||
// Struct maps value of <v> to <objPointer>.
|
||||
// The param <objPointer> should be a pointer to a struct instance.
|
||||
// The param <attrMapping> is used to specify the key-to-attribute mapping rules.
|
||||
func (v *Var) Struct(objPointer interface{}, attrMapping...map[string]string) error {
|
||||
return gconv.Struct(v.Val(), objPointer, attrMapping...)
|
||||
}
|
||||
|
||||
func (v *Var) IsNil() bool { return v.Val() == nil }
|
||||
func (v *Var) Bytes() []byte { return gconv.Bytes(v.Val()) }
|
||||
func (v *Var) String() string { return gconv.String(v.Val()) }
|
||||
@ -88,19 +109,3 @@ func (v *Var) Ints() []int { return gconv.Ints(v.Val()) }
|
||||
func (v *Var) Floats() []float64 { return gconv.Floats(v.Val()) }
|
||||
func (v *Var) Strings() []string { return gconv.Strings(v.Val()) }
|
||||
func (v *Var) Interfaces() []interface{} { return gconv.Interfaces(v.Val()) }
|
||||
|
||||
func (v *Var) Time(format...string) time.Time {
|
||||
return gconv.Time(v.Val(), format...)
|
||||
}
|
||||
func (v *Var) TimeDuration() time.Duration {
|
||||
return gconv.TimeDuration(v.Val())
|
||||
}
|
||||
|
||||
func (v *Var) GTime(format...string) *gtime.Time {
|
||||
return gconv.GTime(v.Val(), format...)
|
||||
}
|
||||
|
||||
// 将变量转换为对象,注意 objPointer 参数必须为struct指针
|
||||
func (v *Var) Struct(objPointer interface{}, attrMapping...map[string]string) error {
|
||||
return gconv.Struct(v.Val(), objPointer, attrMapping...)
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). 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 gvar
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 只读变量接口
|
||||
type VarRead interface {
|
||||
Val() interface{}
|
||||
IsNil() bool
|
||||
Bytes() []byte
|
||||
String() string
|
||||
Bool() bool
|
||||
Int() int
|
||||
Int8() int8
|
||||
Int16() int16
|
||||
Int32() int32
|
||||
Int64() int64
|
||||
Uint() uint
|
||||
Uint8() uint8
|
||||
Uint16() uint16
|
||||
Uint32() uint32
|
||||
Uint64() uint64
|
||||
Float32() float32
|
||||
Float64() float64
|
||||
Interface() interface{}
|
||||
Ints() []int
|
||||
Floats() []float64
|
||||
Strings() []string
|
||||
Interfaces() []interface{}
|
||||
Time(format ...string) time.Time
|
||||
TimeDuration() time.Duration
|
||||
GTime(format...string) *gtime.Time
|
||||
Struct(objPointer interface{}, attrMapping ...map[string]string) error
|
||||
}
|
||||
255
g/container/gvar/gvar_test.go
Normal file
255
g/container/gvar/gvar_test.go
Normal file
@ -0,0 +1,255 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). 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 gvar_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func TestSet(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
objOne := gvar.New("old", true)
|
||||
objOneOld, _ := objOne.Set("new").(string)
|
||||
gtest.Assert(objOneOld, "old")
|
||||
|
||||
objTwo := gvar.New("old", false)
|
||||
objTwoOld, _ := objTwo.Set("new").(string)
|
||||
gtest.Assert(objTwoOld, "old")
|
||||
})
|
||||
}
|
||||
|
||||
func TestVal(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
objOne := gvar.New(1, true)
|
||||
objOneOld, _ := objOne.Val().(int)
|
||||
gtest.Assert(objOneOld, 1)
|
||||
|
||||
objTwo := gvar.New(1, false)
|
||||
objTwoOld, _ := objTwo.Val().(int)
|
||||
gtest.Assert(objTwoOld, 1)
|
||||
})
|
||||
}
|
||||
func TestInterface(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
objOne := gvar.New(1, true)
|
||||
objOneOld, _ := objOne.Interface().(int)
|
||||
gtest.Assert(objOneOld, 1)
|
||||
|
||||
objTwo := gvar.New(1, false)
|
||||
objTwoOld, _ := objTwo.Interface().(int)
|
||||
gtest.Assert(objTwoOld, 1)
|
||||
})
|
||||
}
|
||||
func TestIsNil(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
objOne := gvar.New(nil, true)
|
||||
gtest.Assert(objOne.IsNil(), true)
|
||||
|
||||
objTwo := gvar.New("noNil", false)
|
||||
gtest.Assert(objTwo.IsNil(), false)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestBytes(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
x := int32(1)
|
||||
bytesBuffer := bytes.NewBuffer([]byte{})
|
||||
binary.Write(bytesBuffer, binary.BigEndian, x)
|
||||
|
||||
objOne := gvar.New(bytesBuffer.Bytes(), true)
|
||||
|
||||
bBuf := bytes.NewBuffer(objOne.Bytes())
|
||||
var y int32
|
||||
binary.Read(bBuf, binary.BigEndian, &y)
|
||||
|
||||
gtest.Assert(x, y)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var str string = "hello"
|
||||
objOne := gvar.New(str, true)
|
||||
gtest.Assert(objOne.String(), str)
|
||||
|
||||
})
|
||||
}
|
||||
func TestBool(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var ok bool = true
|
||||
objOne := gvar.New(ok, true)
|
||||
gtest.Assert(objOne.Bool(), ok)
|
||||
|
||||
ok = false
|
||||
objTwo := gvar.New(ok, true)
|
||||
gtest.Assert(objTwo.Bool(), ok)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var num int = 1
|
||||
objOne := gvar.New(num, true)
|
||||
gtest.Assert(objOne.Int(), num)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt8(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var num int8 = 1
|
||||
objOne := gvar.New(num, true)
|
||||
gtest.Assert(objOne.Int8(), num)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt16(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var num int16 = 1
|
||||
objOne := gvar.New(num, true)
|
||||
gtest.Assert(objOne.Int16(), num)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt32(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var num int32 = 1
|
||||
objOne := gvar.New(num, true)
|
||||
gtest.Assert(objOne.Int32(), num)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt64(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var num int64 = 1
|
||||
objOne := gvar.New(num, true)
|
||||
gtest.Assert(objOne.Int64(), num)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestUint(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var num uint = 1
|
||||
objOne := gvar.New(num, true)
|
||||
gtest.Assert(objOne.Uint(), num)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestUint8(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var num uint8 = 1
|
||||
objOne := gvar.New(num, true)
|
||||
gtest.Assert(objOne.Uint8(), num)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestUint16(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var num uint16 = 1
|
||||
objOne := gvar.New(num, true)
|
||||
gtest.Assert(objOne.Uint16(), num)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestUint32(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var num uint32 = 1
|
||||
objOne := gvar.New(num, true)
|
||||
gtest.Assert(objOne.Uint32(), num)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestUint64(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var num uint64 = 1
|
||||
objOne := gvar.New(num, true)
|
||||
gtest.Assert(objOne.Uint64(), num)
|
||||
|
||||
})
|
||||
}
|
||||
func TestFloat32(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var num float32 = 1.1
|
||||
objOne := gvar.New(num, true)
|
||||
gtest.Assert(objOne.Float32(), num)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestFloat64(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var num float64 = 1.1
|
||||
objOne := gvar.New(num, true)
|
||||
gtest.Assert(objOne.Float64(), num)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestInts(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var arr = []int{1, 2, 3, 4, 5}
|
||||
objOne := gvar.New(arr, true)
|
||||
gtest.Assert(objOne.Ints()[0], arr[0])
|
||||
})
|
||||
}
|
||||
func TestFloats(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var arr = []float64{1, 2, 3, 4, 5}
|
||||
objOne := gvar.New(arr, true)
|
||||
gtest.Assert(objOne.Floats()[0], arr[0])
|
||||
})
|
||||
}
|
||||
func TestStrings(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var arr = []string{"hello", "world"}
|
||||
objOne := gvar.New(arr, true)
|
||||
gtest.Assert(objOne.Strings()[0], arr[0])
|
||||
})
|
||||
}
|
||||
|
||||
func TestTime(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
var timeUnix int64 = 1556242660
|
||||
objOne := gvar.New(timeUnix, true)
|
||||
gtest.Assert(objOne.Time().Unix(), timeUnix)
|
||||
})
|
||||
}
|
||||
|
||||
type StTest struct {
|
||||
Test int
|
||||
}
|
||||
|
||||
func TestStruct(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
Kv := make(map[string]int, 1)
|
||||
Kv["Test"] = 100
|
||||
|
||||
testObj := &StTest{}
|
||||
|
||||
objOne := gvar.New(Kv, true)
|
||||
|
||||
objOne.Struct(testObj)
|
||||
|
||||
gtest.Assert(testObj.Test, Kv["Test"])
|
||||
})
|
||||
}
|
||||
@ -61,7 +61,10 @@ func Decrypt(cipherText []byte, key []byte, iv...[]byte) ([]byte, error) {
|
||||
blockModel := cipher.NewCBCDecrypter(block, ivValue)
|
||||
plainText := make([]byte, len(cipherText))
|
||||
blockModel.CryptBlocks(plainText, cipherText)
|
||||
plainText = PKCS5UnPadding(plainText)
|
||||
plainText, e := PKCS5UnPadding(plainText, blockSize)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
return plainText, nil
|
||||
}
|
||||
@ -72,8 +75,27 @@ func PKCS5Padding(src []byte, blockSize int) []byte {
|
||||
return append(src, padtext...)
|
||||
}
|
||||
|
||||
func PKCS5UnPadding(src []byte) []byte {
|
||||
func PKCS5UnPadding(src []byte, blockSize int) ([]byte, error) {
|
||||
length := len(src)
|
||||
if blockSize <= 0 {
|
||||
return nil, errors.New("invalid blocklen")
|
||||
}
|
||||
|
||||
if length%blockSize != 0 || length == 0 {
|
||||
return nil, errors.New("invalid data len")
|
||||
}
|
||||
|
||||
unpadding := int(src[length - 1])
|
||||
return src[:(length - unpadding)]
|
||||
}
|
||||
if unpadding > blockSize || unpadding == 0 {
|
||||
return nil, errors.New("invalid padding")
|
||||
}
|
||||
|
||||
padding := src[length - unpadding:]
|
||||
for i := 0; i < unpadding; i++ {
|
||||
if padding[i] != byte(unpadding) {
|
||||
return nil, errors.New("invalid padding")
|
||||
}
|
||||
}
|
||||
|
||||
return src[:(length - unpadding)], nil
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ var (
|
||||
key_16 = []byte("1234567891234567")
|
||||
key_24 = []byte("123456789123456789123456")
|
||||
key_32 = []byte("12345678912345678912345678912345")
|
||||
keys = []byte("12345678912345678912345678912346")
|
||||
)
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
@ -58,5 +59,9 @@ func TestDecrypt(t *testing.T) {
|
||||
decrypt, err = gaes.Decrypt(encrypt, key_32, iv)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(string(decrypt), string(content))
|
||||
|
||||
encrypt, err = gaes.Encrypt(content, key_32, iv)
|
||||
decrypt, err = gaes.Decrypt(encrypt, keys, iv)
|
||||
gtest.Assert(err, "invalid padding")
|
||||
})
|
||||
}
|
||||
|
||||
@ -20,7 +20,6 @@ import (
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/os/gcache"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -158,7 +157,7 @@ const (
|
||||
|
||||
var (
|
||||
// Instance map.
|
||||
instances = gmap.NewStringInterfaceMap()
|
||||
instances = gmap.NewStrAnyMap()
|
||||
)
|
||||
|
||||
// New creates ORM DB object with global configurations.
|
||||
|
||||
@ -15,9 +15,9 @@ import (
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 格式化SQL查询条件
|
||||
@ -137,6 +137,10 @@ func convertParam(value interface{}) interface{} {
|
||||
}
|
||||
switch kind {
|
||||
case reflect.Struct:
|
||||
// 底层数据库引擎支持 time.Time 类型
|
||||
if _, ok := value.(time.Time); ok {
|
||||
return value
|
||||
}
|
||||
return gconv.String(value)
|
||||
}
|
||||
return value
|
||||
|
||||
@ -13,7 +13,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
|
||||
@ -8,8 +8,9 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"database/sql"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
_ "github.com/gogf/gf/third/github.com/gf-third/mysql"
|
||||
)
|
||||
|
||||
// 数据库链接对象
|
||||
@ -26,7 +27,7 @@ func (db *dbMysql) Open (config *ConfigNode) (*sql.DB, error) {
|
||||
source = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true",
|
||||
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset)
|
||||
}
|
||||
if db, err := sql.Open("mysql", source); err == nil {
|
||||
if db, err := sql.Open("gf-mysql", source); err == nil {
|
||||
return db, nil
|
||||
} else {
|
||||
return nil, err
|
||||
|
||||
@ -10,7 +10,6 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
|
||||
@ -6,15 +6,18 @@
|
||||
|
||||
// Package gredis provides convenient client for redis server.
|
||||
//
|
||||
// Redis客户端.
|
||||
// Redis中文手册请参考:http://redisdoc.com/
|
||||
// Redis官方命令请参考:https://redis.io/commands
|
||||
// Redis Client.
|
||||
//
|
||||
// Redis Commands Official: https://redis.io/commands
|
||||
//
|
||||
// Redis Chinese Documentation: http://redisdoc.com/
|
||||
package gredis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/third/github.com/gomodule/redigo/redis"
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/third/github.com/gomodule/redigo/redis"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -23,44 +26,44 @@ const (
|
||||
gDEFAULT_POOL_MAX_LIFE_TIME = 60 * time.Second
|
||||
)
|
||||
|
||||
// Redis客户端(管理连接池)
|
||||
// Redis client.
|
||||
type Redis struct {
|
||||
pool *redis.Pool // 底层连接池
|
||||
group string // 配置分组
|
||||
config Config // 配置对象
|
||||
pool *redis.Pool // Underlying connection pool.
|
||||
group string // Configuration group.
|
||||
config Config // Configuration.
|
||||
}
|
||||
|
||||
// Redis连接对象(连接池中的单个连接)
|
||||
type Conn redis.Conn
|
||||
// Redis connection.
|
||||
type Conn struct {
|
||||
redis.Conn
|
||||
}
|
||||
|
||||
// Redis服务端但节点连接配置信息
|
||||
// Redis configuration.
|
||||
type Config struct {
|
||||
Host string // 地址
|
||||
Port int // 端口
|
||||
Db int // 数据库
|
||||
Pass string // 授权密码
|
||||
MaxIdle int // 最大允许空闲存在的连接数(默认为0表示不存在闲置连接)
|
||||
MaxActive int // 最大连接数量限制(默认为0表示不限制)
|
||||
IdleTimeout time.Duration // 连接最大空闲时间(默认为60秒,不允许设置为0)
|
||||
MaxConnLifetime time.Duration // 连接最长存活时间(默认为60秒,不允许设置为0)
|
||||
Host string
|
||||
Port int
|
||||
Db int
|
||||
Pass string // Password for AUTH.
|
||||
MaxIdle int // Maximum number of connections allowed to be idle (default is 0 means no idle connection)
|
||||
MaxActive int // Maximum number of connections limit (default is 0 means no limit)
|
||||
IdleTimeout time.Duration // Maximum idle time for connection (default is 60 seconds, not allowed to be set to 0)
|
||||
MaxConnLifetime time.Duration // Maximum lifetime of the connection (default is 60 seconds, not allowed to be set to 0)
|
||||
}
|
||||
|
||||
// Redis链接池统计信息
|
||||
// Pool statistics.
|
||||
type PoolStats struct {
|
||||
redis.PoolStats
|
||||
}
|
||||
|
||||
var (
|
||||
// 单例对象Map
|
||||
instances = gmap.NewStringInterfaceMap()
|
||||
// 连接池Map
|
||||
pools = gmap.NewStringInterfaceMap()
|
||||
// Instance map
|
||||
instances = gmap.NewStrAnyMap()
|
||||
// Pool map.
|
||||
pools = gmap.NewStrAnyMap()
|
||||
)
|
||||
|
||||
// New creates a redis client object with given configuration.
|
||||
// Redis client maintains a connection pool automatically.
|
||||
//
|
||||
// 创建redis操作对象,底层根据配置信息公用的连接池(连接池单例)。
|
||||
func New(config Config) *Redis {
|
||||
if config.IdleTimeout == 0 {
|
||||
config.IdleTimeout = gDEFAULT_POOL_IDLE_TIMEOUT
|
||||
@ -79,20 +82,20 @@ func New(config Config) *Redis {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 密码设置
|
||||
// AUTH
|
||||
if len(config.Pass) > 0 {
|
||||
if _, err := c.Do("AUTH", config.Pass); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// 数据库设置
|
||||
// DB
|
||||
if _, err := c.Do("SELECT", config.Db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
},
|
||||
// 在被应用从连接池中获取出来之后,用以测试连接是否可用,
|
||||
// 如果返回error那么关闭该连接对象重新创建新的连接。
|
||||
// After the conn is taken from the connection pool, to test if the connection is available,
|
||||
// If error is returned then it closes the connection object and recreate a new connection.
|
||||
TestOnBorrow: func(c redis.Conn, t time.Time) error {
|
||||
_, err := c.Do("PING")
|
||||
return err
|
||||
@ -104,9 +107,7 @@ func New(config Config) *Redis {
|
||||
|
||||
// Instance returns an instance of redis client with specified group.
|
||||
// The <group> param is unnecessary, if <group> is not passed,
|
||||
// return redis instance with default group.
|
||||
//
|
||||
// 获取指定分组名称的Redis单例对象,底层根据配置信息公用的连接池(连接池单例)。
|
||||
// it returns a redis instance with default group.
|
||||
func Instance(name ...string) *Redis {
|
||||
group := DEFAULT_GROUP_NAME
|
||||
if len(name) > 0 {
|
||||
@ -128,85 +129,73 @@ func Instance(name ...string) *Redis {
|
||||
|
||||
// Close closes the redis connection pool,
|
||||
// it will release all connections reserved by this pool.
|
||||
// It always not necessary to call Close manually.
|
||||
//
|
||||
// 关闭redis管理对象,将会关闭底层的连接池。
|
||||
// 往往没必要手动调用,跟随进程销毁即可。
|
||||
// It is not necessary to call Close manually.
|
||||
func (r *Redis) Close() error {
|
||||
if r.group != "" {
|
||||
// 如果是单例对象,那么需要从单例对象Map中删除
|
||||
// If it is an instance object, it needs to remove it from the instance Map.
|
||||
instances.Remove(r.group)
|
||||
}
|
||||
pools.Remove(fmt.Sprintf("%v", r.config))
|
||||
return r.pool.Close()
|
||||
}
|
||||
|
||||
// See GetConn.
|
||||
func (r *Redis) Conn() Conn {
|
||||
return r.GetConn()
|
||||
|
||||
// Conn returns a raw underlying connection object,
|
||||
// which expose more methods to communicate with server.
|
||||
// **You should call Close function manually if you do not use this connection any further.**
|
||||
func (r *Redis) Conn() *Conn {
|
||||
return &Conn{ r.pool.Get() }
|
||||
}
|
||||
|
||||
// GetConn returns a raw connection object,
|
||||
// which expose more methods communication with server.
|
||||
// **You should call Close function manually if you do not use this connection any further.**
|
||||
//
|
||||
// 获得一个原生的redis连接对象,用于自定义连接操作,
|
||||
// 但是需要注意的是如果不再使用该连接对象时,需要手动Close连接,否则会造成连接数超限。
|
||||
func (r *Redis) GetConn() Conn {
|
||||
return r.pool.Get().(Conn)
|
||||
// Alias of Conn, see Conn.
|
||||
func (r *Redis) GetConn() *Conn {
|
||||
return r.Conn()
|
||||
}
|
||||
|
||||
// SetMaxIdle sets the MaxIdle attribute of the connection pool.
|
||||
//
|
||||
// 设置属性 - MaxIdle
|
||||
func (r *Redis) SetMaxIdle(value int) {
|
||||
r.pool.MaxIdle = value
|
||||
}
|
||||
|
||||
// SetMaxIdle sets the MaxActive attribute of the connection pool.
|
||||
//
|
||||
// 设置属性 - MaxActive
|
||||
// SetMaxActive sets the MaxActive attribute of the connection pool.
|
||||
func (r *Redis) SetMaxActive(value int) {
|
||||
r.pool.MaxActive = value
|
||||
}
|
||||
|
||||
// SetMaxIdle sets the IdleTimeout attribute of the connection pool.
|
||||
//
|
||||
// 设置属性 - IdleTimeout
|
||||
// SetIdleTimeout sets the IdleTimeout attribute of the connection pool.
|
||||
func (r *Redis) SetIdleTimeout(value time.Duration) {
|
||||
r.pool.IdleTimeout = value
|
||||
}
|
||||
|
||||
// SetMaxIdle sets the MaxConnLifetime attribute of the connection pool.
|
||||
//
|
||||
// 设置属性 - MaxConnLifetime
|
||||
// SetMaxConnLifetime sets the MaxConnLifetime attribute of the connection pool.
|
||||
func (r *Redis) SetMaxConnLifetime(value time.Duration) {
|
||||
r.pool.MaxConnLifetime = value
|
||||
}
|
||||
|
||||
// Stats returns pool's statistics.
|
||||
//
|
||||
// 获取当前连接池统计信息。
|
||||
func (r *Redis) Stats() *PoolStats {
|
||||
return &PoolStats{r.pool.Stats()}
|
||||
}
|
||||
|
||||
// Do sends a command to the server and returns the received reply.
|
||||
// Do automatically get a connection from pool, and close it when reply received.
|
||||
//
|
||||
// 执行同步命令,自动从连接池中获取连接,使用完毕后关闭连接(丢回连接池),开发者不用自行Close.
|
||||
// It does not really "close" the connection, but drop it back to the connection pool.
|
||||
func (r *Redis) Do(command string, args ...interface{}) (interface{}, error) {
|
||||
conn := r.pool.Get()
|
||||
conn := &Conn{ r.pool.Get() }
|
||||
defer conn.Close()
|
||||
return conn.Do(command, args...)
|
||||
}
|
||||
|
||||
// DoVar returns value from Do as gvar.Var.
|
||||
func (r *Redis) DoVar(command string, args ...interface{}) (*gvar.Var, error) {
|
||||
v, err := r.Do(command, args...)
|
||||
return gvar.New(v, true), err
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// Send writes the command to the client's output buffer.
|
||||
//
|
||||
// 执行异步命令 - Send
|
||||
func (r *Redis) Send(command string, args ...interface{}) error {
|
||||
conn := r.pool.Get()
|
||||
conn := &Conn{ r.pool.Get() }
|
||||
defer conn.Close()
|
||||
return conn.Send(command, args...)
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ const (
|
||||
)
|
||||
var (
|
||||
// Configuration groups.
|
||||
configs = gmap.NewStringInterfaceMap()
|
||||
configs = gmap.NewStrAnyMap()
|
||||
)
|
||||
|
||||
// SetConfig sets the global configuration for specified group.
|
||||
|
||||
21
g/database/gredis/gredis_conn.go
Normal file
21
g/database/gredis/gredis_conn.go
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). 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 gredis
|
||||
|
||||
import "github.com/gogf/gf/g/container/gvar"
|
||||
|
||||
// DoVar returns value from Do as gvar.Var.
|
||||
func (c *Conn) DoVar(command string, args ...interface{}) (*gvar.Var, error) {
|
||||
v, err := c.Do(command, args...)
|
||||
return gvar.New(v, true), err
|
||||
}
|
||||
|
||||
// ReceiveVar receives a single reply as gvar.Var from the Redis server.
|
||||
func (c *Conn) ReceiveVar() (*gvar.Var, error) {
|
||||
v, err := c.Receive()
|
||||
return gvar.New(v, true), err
|
||||
}
|
||||
@ -71,7 +71,7 @@ func Test_Stats(t *testing.T) {
|
||||
redis.SetIdleTimeout(500*time.Millisecond)
|
||||
redis.SetMaxConnLifetime(500*time.Millisecond)
|
||||
|
||||
array := make([]gredis.Conn, 0)
|
||||
array := make([]*gredis.Conn, 0)
|
||||
for i := 0; i < 10; i++ {
|
||||
array = append(array, redis.Conn())
|
||||
}
|
||||
|
||||
51
g/encoding/gbase64/gbase64_test.go
Normal file
51
g/encoding/gbase64/gbase64_test.go
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gbase64_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/encoding/gbase64"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testpair struct {
|
||||
decoded, encoded string
|
||||
}
|
||||
|
||||
var pairs = []testpair{
|
||||
// RFC 3548 examples
|
||||
{"\x14\xfb\x9c\x03\xd9\x7e", "FPucA9l+"},
|
||||
{"\x14\xfb\x9c\x03\xd9", "FPucA9k="},
|
||||
{"\x14\xfb\x9c\x03", "FPucAw=="},
|
||||
|
||||
// RFC 4648 examples
|
||||
{"", ""},
|
||||
{"f", "Zg=="},
|
||||
{"fo", "Zm8="},
|
||||
{"foo", "Zm9v"},
|
||||
{"foob", "Zm9vYg=="},
|
||||
{"fooba", "Zm9vYmE="},
|
||||
{"foobar", "Zm9vYmFy"},
|
||||
|
||||
// Wikipedia examples
|
||||
{"sure.", "c3VyZS4="},
|
||||
{"sure", "c3VyZQ=="},
|
||||
{"sur", "c3Vy"},
|
||||
{"su", "c3U="},
|
||||
{"leasure.", "bGVhc3VyZS4="},
|
||||
{"easure.", "ZWFzdXJlLg=="},
|
||||
{"asure.", "YXN1cmUu"},
|
||||
{"sure.", "c3VyZS4="},
|
||||
}
|
||||
|
||||
func TestBase64(t *testing.T) {
|
||||
for k := range pairs{
|
||||
gtest.Assert(gbase64.Encode(pairs[k].decoded), pairs[k].encoded)
|
||||
|
||||
e, _ := gbase64.Decode(pairs[k].encoded)
|
||||
gtest.Assert(e, pairs[k].decoded)
|
||||
}
|
||||
}
|
||||
@ -5,13 +5,15 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gbinary provides useful API for handling binary/bytes data.
|
||||
//
|
||||
// 注意gbinary模块统一使用LittleEndian进行编码。
|
||||
package gbinary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// 二进制位(0|1)
|
||||
@ -20,304 +22,308 @@ type Bit int8
|
||||
// 针对基本类型进行二进制打包,支持的基本数据类型包括:int/8/16/32/64、uint/8/16/32/64、float32/64、bool、string、[]byte
|
||||
// 其他未知类型使用 fmt.Sprintf("%v", value) 转换为字符串之后处理
|
||||
func Encode(vs ...interface{}) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
for i := 0; i < len(vs); i++ {
|
||||
switch value := vs[i].(type) {
|
||||
case int: buf.Write(EncodeInt(value))
|
||||
case int8: buf.Write(EncodeInt8(value))
|
||||
case int16: buf.Write(EncodeInt16(value))
|
||||
case int32: buf.Write(EncodeInt32(value))
|
||||
case int64: buf.Write(EncodeInt64(value))
|
||||
case uint: buf.Write(EncodeUint(value))
|
||||
case uint8: buf.Write(EncodeUint8(value))
|
||||
case uint16: buf.Write(EncodeUint16(value))
|
||||
case uint32: buf.Write(EncodeUint32(value))
|
||||
case uint64: buf.Write(EncodeUint64(value))
|
||||
case bool: buf.Write(EncodeBool(value))
|
||||
case string: buf.Write(EncodeString(value))
|
||||
case []byte: buf.Write(value)
|
||||
case float32: buf.Write(EncodeFloat32(value))
|
||||
case float64: buf.Write(EncodeFloat64(value))
|
||||
default:
|
||||
if err := binary.Write(buf, binary.LittleEndian, value); err != nil {
|
||||
buf.Write(EncodeString(fmt.Sprintf("%v", value)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.Bytes()
|
||||
buf := new(bytes.Buffer)
|
||||
for i := 0; i < len(vs); i++ {
|
||||
if vs[i] == nil {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
switch value := vs[i].(type) {
|
||||
case int: buf.Write(EncodeInt(value))
|
||||
case int8: buf.Write(EncodeInt8(value))
|
||||
case int16: buf.Write(EncodeInt16(value))
|
||||
case int32: buf.Write(EncodeInt32(value))
|
||||
case int64: buf.Write(EncodeInt64(value))
|
||||
case uint: buf.Write(EncodeUint(value))
|
||||
case uint8: buf.Write(EncodeUint8(value))
|
||||
case uint16: buf.Write(EncodeUint16(value))
|
||||
case uint32: buf.Write(EncodeUint32(value))
|
||||
case uint64: buf.Write(EncodeUint64(value))
|
||||
case bool: buf.Write(EncodeBool(value))
|
||||
case string: buf.Write(EncodeString(value))
|
||||
case []byte: buf.Write(value)
|
||||
case float32: buf.Write(EncodeFloat32(value))
|
||||
case float64: buf.Write(EncodeFloat64(value))
|
||||
default:
|
||||
if err := binary.Write(buf, binary.LittleEndian, value); err != nil {
|
||||
buf.Write(EncodeString(fmt.Sprintf("%v", value)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// 将变量转换为二进制[]byte,并指定固定的[]byte长度返回,长度单位为字节(byte);
|
||||
// 如果转换的二进制长度超过指定长度,那么进行截断处理
|
||||
func EncodeByLength(length int, vs ...interface{}) []byte {
|
||||
b := Encode(vs...)
|
||||
if len(b) < length {
|
||||
b = append(b, make([]byte, length - len(b))...)
|
||||
} else if len(b) > length {
|
||||
b = b[0 : length]
|
||||
}
|
||||
return b
|
||||
b := Encode(vs...)
|
||||
if len(b) < length {
|
||||
b = append(b, make([]byte, length - len(b))...)
|
||||
} else if len(b) > length {
|
||||
b = b[0 : length]
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// 整形二进制解包,注意第二个及其后参数为字长确定的整形变量的指针地址,以便确定解析的[]byte长度,
|
||||
// 例如:int8/16/32/64、uint8/16/32/64、float32/64等等
|
||||
func Decode(b []byte, vs ...interface{}) error {
|
||||
buf := bytes.NewBuffer(b)
|
||||
for i := 0; i < len(vs); i++ {
|
||||
err := binary.Read(buf, binary.LittleEndian, vs[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
buf := bytes.NewBuffer(b)
|
||||
for i := 0; i < len(vs); i++ {
|
||||
err := binary.Read(buf, binary.LittleEndian, vs[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func EncodeString(s string) []byte {
|
||||
return []byte(s)
|
||||
return []byte(s)
|
||||
}
|
||||
|
||||
func DecodeToString(b []byte) string {
|
||||
return string(b)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func EncodeBool(b bool) []byte {
|
||||
if b == true {
|
||||
return []byte{1}
|
||||
} else {
|
||||
return []byte{0}
|
||||
}
|
||||
if b == true {
|
||||
return []byte{1}
|
||||
} else {
|
||||
return []byte{0}
|
||||
}
|
||||
}
|
||||
|
||||
// 自动识别int类型长度,转换为[]byte
|
||||
func EncodeInt(i int) []byte {
|
||||
if i <= math.MaxInt8 {
|
||||
return EncodeInt8(int8(i))
|
||||
} else if i <= math.MaxInt16 {
|
||||
return EncodeInt16(int16(i))
|
||||
} else if i <= math.MaxInt32 {
|
||||
return EncodeInt32(int32(i))
|
||||
} else {
|
||||
return EncodeInt64(int64(i))
|
||||
}
|
||||
if i <= math.MaxInt8 {
|
||||
return EncodeInt8(int8(i))
|
||||
} else if i <= math.MaxInt16 {
|
||||
return EncodeInt16(int16(i))
|
||||
} else if i <= math.MaxInt32 {
|
||||
return EncodeInt32(int32(i))
|
||||
} else {
|
||||
return EncodeInt64(int64(i))
|
||||
}
|
||||
}
|
||||
|
||||
// 自动识别uint类型长度,转换为[]byte
|
||||
func EncodeUint(i uint) []byte {
|
||||
if i <= math.MaxUint8 {
|
||||
return EncodeUint8(uint8(i))
|
||||
} else if i <= math.MaxUint16 {
|
||||
return EncodeUint16(uint16(i))
|
||||
} else if i <= math.MaxUint32 {
|
||||
return EncodeUint32(uint32(i))
|
||||
} else {
|
||||
return EncodeUint64(uint64(i))
|
||||
}
|
||||
if i <= math.MaxUint8 {
|
||||
return EncodeUint8(uint8(i))
|
||||
} else if i <= math.MaxUint16 {
|
||||
return EncodeUint16(uint16(i))
|
||||
} else if i <= math.MaxUint32 {
|
||||
return EncodeUint32(uint32(i))
|
||||
} else {
|
||||
return EncodeUint64(uint64(i))
|
||||
}
|
||||
}
|
||||
|
||||
func EncodeInt8(i int8) []byte {
|
||||
return []byte{byte(i)}
|
||||
return []byte{byte(i)}
|
||||
}
|
||||
|
||||
func EncodeUint8(i uint8) []byte {
|
||||
return []byte{byte(i)}
|
||||
return []byte{byte(i)}
|
||||
}
|
||||
|
||||
func EncodeInt16(i int16) []byte {
|
||||
bytes := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(bytes, uint16(i))
|
||||
return bytes
|
||||
bytes := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(bytes, uint16(i))
|
||||
return bytes
|
||||
}
|
||||
|
||||
func EncodeUint16(i uint16) []byte {
|
||||
bytes := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(bytes, i)
|
||||
return bytes
|
||||
bytes := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(bytes, i)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func EncodeInt32(i int32) []byte {
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, uint32(i))
|
||||
return bytes
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, uint32(i))
|
||||
return bytes
|
||||
}
|
||||
|
||||
func EncodeUint32(i uint32) []byte {
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, i)
|
||||
return bytes
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, i)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func EncodeInt64(i int64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, uint64(i))
|
||||
return bytes
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, uint64(i))
|
||||
return bytes
|
||||
}
|
||||
|
||||
func EncodeUint64(i uint64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, i)
|
||||
return bytes
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, i)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func EncodeFloat32(f float32) []byte {
|
||||
bits := math.Float32bits(f)
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, bits)
|
||||
return bytes
|
||||
bits := math.Float32bits(f)
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, bits)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func EncodeFloat64(f float64) []byte {
|
||||
bits := math.Float64bits(f)
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, bits)
|
||||
return bytes
|
||||
bits := math.Float64bits(f)
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, bits)
|
||||
return bytes
|
||||
}
|
||||
|
||||
// 当b位数不够时,进行高位补0
|
||||
func fillUpSize(b []byte, l int) []byte {
|
||||
if len(b) >= l {
|
||||
return b
|
||||
}
|
||||
c := make([]byte, 0)
|
||||
c = append(c, b...)
|
||||
for i := 0; i < l - len(b); i++ {
|
||||
c = append(c, 0x00)
|
||||
}
|
||||
return c
|
||||
if len(b) >= l {
|
||||
return b
|
||||
}
|
||||
c := make([]byte, 0)
|
||||
c = append(c, b...)
|
||||
for i := 0; i < l - len(b); i++ {
|
||||
c = append(c, 0x00)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// 将二进制解析为int类型,根据[]byte的长度进行自动转换.
|
||||
// 注意内部使用的是uint*,使用int会造成位丢失。
|
||||
func DecodeToInt(b []byte) int {
|
||||
if len(b) < 2 {
|
||||
return int(DecodeToUint8(b))
|
||||
} else if len(b) < 3 {
|
||||
return int(DecodeToUint16(b))
|
||||
} else if len(b) < 5 {
|
||||
return int(DecodeToUint32(b))
|
||||
} else {
|
||||
return int(DecodeToUint64(b))
|
||||
}
|
||||
if len(b) < 2 {
|
||||
return int(DecodeToUint8(b))
|
||||
} else if len(b) < 3 {
|
||||
return int(DecodeToUint16(b))
|
||||
} else if len(b) < 5 {
|
||||
return int(DecodeToUint32(b))
|
||||
} else {
|
||||
return int(DecodeToUint64(b))
|
||||
}
|
||||
}
|
||||
|
||||
// 将二进制解析为uint类型,根据[]byte的长度进行自动转换
|
||||
func DecodeToUint(b []byte) uint {
|
||||
if len(b) < 2 {
|
||||
return uint(DecodeToUint8(b))
|
||||
} else if len(b) < 3 {
|
||||
return uint(DecodeToUint16(b))
|
||||
} else if len(b) < 5 {
|
||||
return uint(DecodeToUint32(b))
|
||||
} else {
|
||||
return uint(DecodeToUint64(b))
|
||||
}
|
||||
if len(b) < 2 {
|
||||
return uint(DecodeToUint8(b))
|
||||
} else if len(b) < 3 {
|
||||
return uint(DecodeToUint16(b))
|
||||
} else if len(b) < 5 {
|
||||
return uint(DecodeToUint32(b))
|
||||
} else {
|
||||
return uint(DecodeToUint64(b))
|
||||
}
|
||||
}
|
||||
|
||||
// 将二进制解析为bool类型,识别标准是判断二进制中数值是否都为0,或者为空
|
||||
func DecodeToBool(b []byte) bool {
|
||||
if len(b) == 0 {
|
||||
return false
|
||||
}
|
||||
if bytes.Compare(b, make([]byte, len(b))) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
if len(b) == 0 {
|
||||
return false
|
||||
}
|
||||
if bytes.Compare(b, make([]byte, len(b))) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func DecodeToInt8(b []byte) int8 {
|
||||
return int8(b[0])
|
||||
return int8(b[0])
|
||||
}
|
||||
|
||||
func DecodeToUint8(b []byte) uint8 {
|
||||
return uint8(b[0])
|
||||
return uint8(b[0])
|
||||
}
|
||||
|
||||
func DecodeToInt16(b []byte) int16 {
|
||||
return int16(binary.LittleEndian.Uint16(fillUpSize(b, 2)))
|
||||
return int16(binary.LittleEndian.Uint16(fillUpSize(b, 2)))
|
||||
}
|
||||
|
||||
func DecodeToUint16(b []byte) uint16 {
|
||||
return binary.LittleEndian.Uint16(fillUpSize(b, 2))
|
||||
return binary.LittleEndian.Uint16(fillUpSize(b, 2))
|
||||
}
|
||||
|
||||
func DecodeToInt32(b []byte) int32 {
|
||||
return int32(binary.LittleEndian.Uint32(fillUpSize(b, 4)))
|
||||
return int32(binary.LittleEndian.Uint32(fillUpSize(b, 4)))
|
||||
}
|
||||
|
||||
func DecodeToUint32(b []byte) uint32 {
|
||||
return binary.LittleEndian.Uint32(fillUpSize(b, 4))
|
||||
return binary.LittleEndian.Uint32(fillUpSize(b, 4))
|
||||
}
|
||||
|
||||
func DecodeToInt64(b []byte) int64 {
|
||||
return int64(binary.LittleEndian.Uint64(fillUpSize(b, 8)))
|
||||
return int64(binary.LittleEndian.Uint64(fillUpSize(b, 8)))
|
||||
}
|
||||
|
||||
func DecodeToUint64(b []byte) uint64 {
|
||||
return binary.LittleEndian.Uint64(fillUpSize(b, 8))
|
||||
return binary.LittleEndian.Uint64(fillUpSize(b, 8))
|
||||
}
|
||||
|
||||
func DecodeToFloat32(b []byte) float32 {
|
||||
return math.Float32frombits(binary.LittleEndian.Uint32(fillUpSize(b, 4)))
|
||||
return math.Float32frombits(binary.LittleEndian.Uint32(fillUpSize(b, 4)))
|
||||
}
|
||||
|
||||
func DecodeToFloat64(b []byte) float64 {
|
||||
return math.Float64frombits(binary.LittleEndian.Uint64(fillUpSize(b, 8)))
|
||||
return math.Float64frombits(binary.LittleEndian.Uint64(fillUpSize(b, 8)))
|
||||
}
|
||||
|
||||
// 默认编码
|
||||
func EncodeBits(bits []Bit, i int, l int) []Bit {
|
||||
return EncodeBitsWithUint(bits, uint(i), l)
|
||||
return EncodeBitsWithUint(bits, uint(i), l)
|
||||
}
|
||||
|
||||
// 将ui按位合并到bits数组中,并占length长度位(注意:uis数组中存放的是二进制的0|1数字)
|
||||
func EncodeBitsWithUint(bits []Bit, ui uint, l int) []Bit {
|
||||
a := make([]Bit, l)
|
||||
for i := l - 1; i >= 0; i-- {
|
||||
a[i] = Bit(ui & 1)
|
||||
ui >>= 1
|
||||
}
|
||||
if bits != nil {
|
||||
return append(bits, a...)
|
||||
} else {
|
||||
return a
|
||||
}
|
||||
a := make([]Bit, l)
|
||||
for i := l - 1; i >= 0; i-- {
|
||||
a[i] = Bit(ui & 1)
|
||||
ui >>= 1
|
||||
}
|
||||
if bits != nil {
|
||||
return append(bits, a...)
|
||||
} else {
|
||||
return a
|
||||
}
|
||||
}
|
||||
// 将bits转换为[]byte,从左至右进行编码,不足1 byte按0往末尾补充
|
||||
func EncodeBitsToBytes(bits []Bit) []byte {
|
||||
if len(bits)%8 != 0 {
|
||||
for i := 0; i < len(bits)%8; i++ {
|
||||
bits = append(bits, 0)
|
||||
}
|
||||
}
|
||||
b := make([]byte, 0)
|
||||
for i := 0; i < len(bits); i += 8 {
|
||||
b = append(b, byte(DecodeBitsToUint(bits[i : i + 8])))
|
||||
}
|
||||
return b
|
||||
if len(bits)%8 != 0 {
|
||||
for i := 0; i < len(bits)%8; i++ {
|
||||
bits = append(bits, 0)
|
||||
}
|
||||
}
|
||||
b := make([]byte, 0)
|
||||
for i := 0; i < len(bits); i += 8 {
|
||||
b = append(b, byte(DecodeBitsToUint(bits[i : i + 8])))
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// 解析为int
|
||||
func DecodeBits(bits []Bit) int {
|
||||
v := int(0)
|
||||
for _, i := range bits {
|
||||
v = v << 1 | int(i)
|
||||
}
|
||||
return v
|
||||
v := int(0)
|
||||
for _, i := range bits {
|
||||
v = v << 1 | int(i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// 解析为uint
|
||||
func DecodeBitsToUint(bits []Bit) uint {
|
||||
v := uint(0)
|
||||
for _, i := range bits {
|
||||
v = v << 1 | uint(i)
|
||||
}
|
||||
return v
|
||||
v := uint(0)
|
||||
for _, i := range bits {
|
||||
v = v << 1 | uint(i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// 解析[]byte为字位数组[]uint8
|
||||
func DecodeBytesToBits(bs []byte) []Bit {
|
||||
bits := make([]Bit, 0)
|
||||
for _, b := range bs {
|
||||
bits = EncodeBitsWithUint(bits, uint(b), 8)
|
||||
}
|
||||
return bits
|
||||
bits := make([]Bit, 0)
|
||||
for _, b := range bs {
|
||||
bits = EncodeBitsWithUint(bits, uint(b), 8)
|
||||
}
|
||||
return bits
|
||||
}
|
||||
131
g/encoding/gbinary/gbinary_test.go
Normal file
131
g/encoding/gbinary/gbinary_test.go
Normal file
@ -0,0 +1,131 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gbinary_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/encoding/gbinary"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testData = map[string]interface{}{
|
||||
//"nil": nil,
|
||||
"int": int(123),
|
||||
"int8": int8(-99),
|
||||
"int8.max": math.MaxInt8,
|
||||
"int16": int16(123),
|
||||
"int16.max": math.MaxInt16,
|
||||
"int32": int32(-199),
|
||||
"int32.max": math.MaxInt32,
|
||||
"int64": int64(123),
|
||||
"uint": uint(123),
|
||||
"uint8": uint8(123),
|
||||
"uint8.max": math.MaxUint8,
|
||||
"uint16": uint16(9999),
|
||||
"uint16.max": math.MaxUint16,
|
||||
"uint32": uint32(123),
|
||||
"uint64": uint64(123),
|
||||
"bool.true": true,
|
||||
"bool.false": false,
|
||||
"string": "hehe haha",
|
||||
"byte": []byte("hehe haha"),
|
||||
"float32": float32(123.456),
|
||||
"float32.max": math.MaxFloat32,
|
||||
"float64": float64(123.456),
|
||||
}
|
||||
|
||||
func TestEncodeAndDecode(t *testing.T) {
|
||||
for k, v := range testData {
|
||||
ve := gbinary.Encode(v)
|
||||
ve1 := gbinary.EncodeByLength(len(ve), v)
|
||||
|
||||
//t.Logf("%s:%v, encoded:%v\n", k, v, ve)
|
||||
switch v.(type) {
|
||||
case int:
|
||||
gtest.Assert(gbinary.DecodeToInt(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToInt(ve1), v)
|
||||
case int8:
|
||||
gtest.Assert(gbinary.DecodeToInt8(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToInt8(ve1), v)
|
||||
case int16:
|
||||
gtest.Assert(gbinary.DecodeToInt16(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToInt16(ve1), v)
|
||||
case int32:
|
||||
gtest.Assert(gbinary.DecodeToInt32(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToInt32(ve1), v)
|
||||
case int64:
|
||||
gtest.Assert(gbinary.DecodeToInt64(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToInt64(ve1), v)
|
||||
case uint:
|
||||
gtest.Assert(gbinary.DecodeToUint(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToUint(ve1), v)
|
||||
case uint8:
|
||||
gtest.Assert(gbinary.DecodeToUint8(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToUint8(ve1), v)
|
||||
case uint16:
|
||||
gtest.Assert(gbinary.DecodeToUint16(ve1), v)
|
||||
gtest.Assert(gbinary.DecodeToUint16(ve), v)
|
||||
case uint32:
|
||||
gtest.Assert(gbinary.DecodeToUint32(ve1), v)
|
||||
gtest.Assert(gbinary.DecodeToUint32(ve), v)
|
||||
case uint64:
|
||||
gtest.Assert(gbinary.DecodeToUint64(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToUint64(ve1), v)
|
||||
case bool:
|
||||
gtest.Assert(gbinary.DecodeToBool(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToBool(ve1), v)
|
||||
case string:
|
||||
gtest.Assert(gbinary.DecodeToString(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToString(ve1), v)
|
||||
case float32:
|
||||
gtest.Assert(gbinary.DecodeToFloat32(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToFloat32(ve1), v)
|
||||
case float64:
|
||||
gtest.Assert(gbinary.DecodeToFloat64(ve), v)
|
||||
gtest.Assert(gbinary.DecodeToFloat64(ve1), v)
|
||||
default:
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
res := make([]byte, len(ve))
|
||||
err := gbinary.Decode(ve, res)
|
||||
if err != nil {
|
||||
t.Errorf("test data: %s, %v, error:%v", k, v, err)
|
||||
}
|
||||
gtest.Assert(res, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
Age int
|
||||
Url string
|
||||
}
|
||||
|
||||
func TestEncodeStruct(t *testing.T) {
|
||||
user := User{"wenzi1", 999, "www.baidu.com"}
|
||||
ve := gbinary.Encode(user)
|
||||
s := gbinary.DecodeToString(ve)
|
||||
gtest.Assert(string(s), s)
|
||||
}
|
||||
|
||||
var testBitData = []int{0, 99, 122, 129, 222, 999, 22322}
|
||||
|
||||
func TestBits(t *testing.T) {
|
||||
for i := range testBitData {
|
||||
bits := make([]gbinary.Bit, 0)
|
||||
res := gbinary.EncodeBits(bits, testBitData[i], 64)
|
||||
|
||||
gtest.Assert(gbinary.DecodeBits(res), testBitData[i])
|
||||
gtest.Assert(gbinary.DecodeBitsToUint(res), uint(testBitData[i]))
|
||||
|
||||
gtest.Assert(gbinary.DecodeBytesToBits(gbinary.EncodeBitsToBytes(res)), res)
|
||||
}
|
||||
|
||||
}
|
||||
42
g/encoding/gcompress/gcompress_test.go
Normal file
42
g/encoding/gcompress/gcompress_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gcompress_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/encoding/gcompress"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestZlib(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
src := "hello, world\n"
|
||||
dst := []byte{120, 156, 202, 72, 205, 201, 201, 215, 81, 40, 207, 47, 202, 73, 225, 2, 4, 0, 0, 255, 255, 33, 231, 4, 147}
|
||||
gtest.Assert(gcompress.Zlib([]byte(src)), dst)
|
||||
|
||||
gtest.Assert(gcompress.UnZlib(dst), []byte(src))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestGzip(t *testing.T) {
|
||||
src := "Hello World!!"
|
||||
|
||||
gzip := []byte{
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff,
|
||||
0xf2, 0x48, 0xcd, 0xc9, 0xc9,
|
||||
0x57, 0x08, 0xcf, 0x2f, 0xca,
|
||||
0x49, 0x51, 0x54, 0x04, 0x04,
|
||||
0x00, 0x00, 0xff, 0xff, 0x9d,
|
||||
0x24, 0xa8, 0xd1, 0x0d, 0x00,
|
||||
0x00, 0x00,
|
||||
}
|
||||
|
||||
gtest.Assert(gcompress.Gzip([]byte(src)), gzip)
|
||||
|
||||
gtest.Assert(gcompress.UnGzip(gzip), []byte(src))
|
||||
}
|
||||
32
g/encoding/ghtml/ghtml_test.go
Normal file
32
g/encoding/ghtml/ghtml_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 ghtml_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/encoding/ghtml"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStripTags(t *testing.T) {
|
||||
src := `<p>Test paragraph.</p><!-- Comment --> <a href="#fragment">Other text</a>`
|
||||
dst := `Test paragraph. Other text`
|
||||
gtest.Assert(ghtml.StripTags(src), dst)
|
||||
}
|
||||
|
||||
func TestEntities(t *testing.T) {
|
||||
src := `A 'quote' "is" <b>bold</b>`
|
||||
dst := `A 'quote' "is" <b>bold</b>`
|
||||
gtest.Assert(ghtml.Entities(src), dst)
|
||||
gtest.Assert(ghtml.EntitiesDecode(dst), src)
|
||||
}
|
||||
|
||||
func TestSpecialChars(t *testing.T) {
|
||||
src := `A 'quote' "is" <b>bold</b>`
|
||||
dst := `A 'quote' "is" <b>bold</b>`
|
||||
gtest.Assert(ghtml.SpecialChars(src), dst)
|
||||
gtest.Assert(ghtml.SpecialCharsDecode(dst), src)
|
||||
}
|
||||
@ -8,22 +8,12 @@
|
||||
package gjson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/encoding/gtoml"
|
||||
"github.com/gogf/gf/g/encoding/gxml"
|
||||
"github.com/gogf/gf/g/encoding/gyaml"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/os/gfcache"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -40,330 +30,6 @@ type Json struct {
|
||||
// when the hierarchical data key contains separator char.
|
||||
}
|
||||
|
||||
// New creates a Json object with any variable type of <data>,
|
||||
// but <data> should be a map or slice for data access reason,
|
||||
// or it will make no sense.
|
||||
// The <unsafe> param specifies whether using this Json object
|
||||
// in un-concurrent-safe context, which is false in default.
|
||||
func New(data interface{}, unsafe...bool) *Json {
|
||||
j := (*Json)(nil)
|
||||
switch data.(type) {
|
||||
case map[string]interface{}, []interface{}, nil:
|
||||
j = &Json {
|
||||
p : &data,
|
||||
c : byte(gDEFAULT_SPLIT_CHAR),
|
||||
vc : false ,
|
||||
}
|
||||
case string, []byte:
|
||||
j, _ = LoadContent(gconv.Bytes(data))
|
||||
default:
|
||||
v := (interface{})(nil)
|
||||
if m := gconv.Map(data); m != nil {
|
||||
v = m
|
||||
j = &Json {
|
||||
p : &v,
|
||||
c : byte(gDEFAULT_SPLIT_CHAR),
|
||||
vc : false,
|
||||
}
|
||||
} else {
|
||||
v = gconv.Interfaces(data)
|
||||
j = &Json {
|
||||
p : &v,
|
||||
c : byte(gDEFAULT_SPLIT_CHAR),
|
||||
vc : false,
|
||||
}
|
||||
}
|
||||
}
|
||||
j.mu = rwmutex.New(unsafe...)
|
||||
return j
|
||||
}
|
||||
|
||||
// NewUnsafe creates a un-concurrent-safe Json object.
|
||||
func NewUnsafe(data...interface{}) *Json {
|
||||
if len(data) > 0 {
|
||||
return New(data[0], true)
|
||||
}
|
||||
return New(nil, true)
|
||||
}
|
||||
|
||||
// Valid checks whether <data> is a valid JSON data type.
|
||||
func Valid(data interface{}) bool {
|
||||
return json.Valid(gconv.Bytes(data))
|
||||
}
|
||||
|
||||
// Encode encodes <value> to JSON data type of bytes.
|
||||
func Encode(value interface{}) ([]byte, error) {
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
// Decode decodes <data>(string/[]byte) to golang variable.
|
||||
func Decode(data interface{}) (interface{}, error) {
|
||||
var value interface{}
|
||||
if err := DecodeTo(gconv.Bytes(data), &value); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Decode decodes <data>(string/[]byte) to specified golang variable <v>.
|
||||
// The <v> should be a pointer type.
|
||||
func DecodeTo(data interface{}, v interface{}) error {
|
||||
decoder := json.NewDecoder(bytes.NewReader(gconv.Bytes(data)))
|
||||
decoder.UseNumber()
|
||||
return decoder.Decode(v)
|
||||
}
|
||||
|
||||
// DecodeToJson codes <data>(string/[]byte) to a Json object.
|
||||
func DecodeToJson(data interface{}, unsafe...bool) (*Json, error) {
|
||||
if v, err := Decode(gconv.Bytes(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return New(v, unsafe...), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Load loads content from specified file <path>,
|
||||
// and creates a Json object from its content.
|
||||
func Load(path string, unsafe...bool) (*Json, error) {
|
||||
return LoadContent(gfcache.GetBinContents(path), unsafe...)
|
||||
}
|
||||
|
||||
// LoadContent creates a Json object from given content,
|
||||
// it checks the data type of <content> automatically,
|
||||
// supporting JSON, XML, YAML and TOML types of data.
|
||||
func LoadContent(data interface{}, unsafe...bool) (*Json, error) {
|
||||
var err error
|
||||
var result interface{}
|
||||
b := gconv.Bytes(data)
|
||||
t := "json"
|
||||
// auto check data type
|
||||
if json.Valid(b) {
|
||||
t = "json"
|
||||
} else if gregex.IsMatch(`^<.+>.*</.+>$`, b) {
|
||||
t = "xml"
|
||||
} else if gregex.IsMatch(`^[\s\t]*\w+\s*:\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*:\s*.+`, b) {
|
||||
t = "yml"
|
||||
} else if gregex.IsMatch(`^[\s\t]*\w+\s*=\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*=\s*.+`, b) {
|
||||
t = "toml"
|
||||
} else {
|
||||
return nil, errors.New("unsupported data type")
|
||||
}
|
||||
// convert to json type data
|
||||
switch t {
|
||||
case "json", ".json":
|
||||
// ok
|
||||
case "xml", ".xml":
|
||||
// TODO UseNumber
|
||||
b, err = gxml.ToJson(b)
|
||||
|
||||
case "yml", "yaml", ".yml", ".yaml":
|
||||
// TODO UseNumber
|
||||
b, err = gyaml.ToJson(b)
|
||||
|
||||
case "toml", ".toml":
|
||||
// TODO UseNumber
|
||||
b, err = gtoml.ToJson(b)
|
||||
|
||||
default:
|
||||
err = errors.New("nonsupport type " + t)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result == nil {
|
||||
decoder := json.NewDecoder(bytes.NewReader(b))
|
||||
decoder.UseNumber()
|
||||
if err := decoder.Decode(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch result.(type) {
|
||||
case string, []byte:
|
||||
return nil, fmt.Errorf(`json decoding failed for content: %s`, string(b))
|
||||
}
|
||||
}
|
||||
return New(result, unsafe...), nil
|
||||
}
|
||||
|
||||
// SetSplitChar sets the separator char for hierarchical data access.
|
||||
func (j *Json) SetSplitChar(char byte) {
|
||||
j.mu.Lock()
|
||||
j.c = char
|
||||
j.mu.Unlock()
|
||||
}
|
||||
|
||||
// SetViolenceCheck enables/disables violence check for hierarchical data access.
|
||||
func (j *Json) SetViolenceCheck(enabled bool) {
|
||||
j.mu.Lock()
|
||||
j.vc = enabled
|
||||
j.mu.Unlock()
|
||||
}
|
||||
|
||||
// GetToVar gets the value by specified <pattern>,
|
||||
// and converts it to specified golang variable <v>.
|
||||
// The <v> should be a pointer type.
|
||||
func (j *Json) GetToVar(pattern string, v interface{}) error {
|
||||
r := j.Get(pattern)
|
||||
if r != nil {
|
||||
if t, err := Encode(r); err == nil {
|
||||
return DecodeTo(t, v)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
v = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMap gets the value by specified <pattern>,
|
||||
// and converts it to map[string]interface{}.
|
||||
func (j *Json) GetMap(pattern string) map[string]interface{} {
|
||||
result := j.Get(pattern)
|
||||
if result != nil {
|
||||
return gconv.Map(result)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetJson gets the value by specified <pattern>,
|
||||
// and converts it to a Json object.
|
||||
func (j *Json) GetJson(pattern string) *Json {
|
||||
result := j.Get(pattern)
|
||||
if result != nil {
|
||||
return New(result)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetJsons gets the value by specified <pattern>,
|
||||
// and converts it to a slice of Json object.
|
||||
func (j *Json) GetJsons(pattern string) []*Json {
|
||||
array := j.GetArray(pattern)
|
||||
if len(array) > 0 {
|
||||
jsons := make([]*Json, len(array))
|
||||
for i := 0; i < len(array); i++ {
|
||||
jsons[i] = New(array[i], !j.mu.IsSafe())
|
||||
}
|
||||
return jsons
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetArray gets the value by specified <pattern>,
|
||||
// and converts it to a slice of []interface{}.
|
||||
func (j *Json) GetArray(pattern string) []interface{} {
|
||||
return gconv.Interfaces(j.Get(pattern))
|
||||
}
|
||||
|
||||
// GetString gets the value by specified <pattern>,
|
||||
// and converts it to string.
|
||||
func (j *Json) GetString(pattern string) string {
|
||||
return gconv.String(j.Get(pattern))
|
||||
}
|
||||
|
||||
// GetStrings gets the value by specified <pattern>,
|
||||
// and converts it to a slice of []string.
|
||||
func (j *Json) GetStrings(pattern string) []string {
|
||||
return gconv.Strings(j.Get(pattern))
|
||||
}
|
||||
|
||||
// See GetArray.
|
||||
func (j *Json) GetInterfaces(pattern string) []interface{} {
|
||||
return gconv.Interfaces(j.Get(pattern))
|
||||
}
|
||||
|
||||
func (j *Json) GetTime(pattern string, format ... string) time.Time {
|
||||
return gconv.Time(j.Get(pattern), format...)
|
||||
}
|
||||
|
||||
func (j *Json) GetTimeDuration(pattern string) time.Duration {
|
||||
return gconv.TimeDuration(j.Get(pattern))
|
||||
}
|
||||
|
||||
// GetBool gets the value by specified <pattern>,
|
||||
// and converts it to bool.
|
||||
// It returns false when value is: "", 0, false, off, nil;
|
||||
// or returns true instead.
|
||||
func (j *Json) GetBool(pattern string) bool {
|
||||
return gconv.Bool(j.Get(pattern))
|
||||
}
|
||||
|
||||
func (j *Json) GetInt(pattern string) int {
|
||||
return gconv.Int(j.Get(pattern))
|
||||
}
|
||||
|
||||
func (j *Json) GetInt8(pattern string) int8 {
|
||||
return gconv.Int8(j.Get(pattern))
|
||||
}
|
||||
|
||||
func (j *Json) GetInt16(pattern string) int16 {
|
||||
return gconv.Int16(j.Get(pattern))
|
||||
}
|
||||
|
||||
func (j *Json) GetInt32(pattern string) int32 {
|
||||
return gconv.Int32(j.Get(pattern))
|
||||
}
|
||||
|
||||
func (j *Json) GetInt64(pattern string) int64 {
|
||||
return gconv.Int64(j.Get(pattern))
|
||||
}
|
||||
|
||||
func (j *Json) GetInts(pattern string) []int {
|
||||
return gconv.Ints(j.Get(pattern))
|
||||
}
|
||||
|
||||
func (j *Json) GetUint(pattern string) uint {
|
||||
return gconv.Uint(j.Get(pattern))
|
||||
}
|
||||
|
||||
func (j *Json) GetUint8(pattern string) uint8 {
|
||||
return gconv.Uint8(j.Get(pattern))
|
||||
}
|
||||
|
||||
func (j *Json) GetUint16(pattern string) uint16 {
|
||||
return gconv.Uint16(j.Get(pattern))
|
||||
}
|
||||
|
||||
func (j *Json) GetUint32(pattern string) uint32 {
|
||||
return gconv.Uint32(j.Get(pattern))
|
||||
}
|
||||
|
||||
func (j *Json) GetUint64(pattern string) uint64 {
|
||||
return gconv.Uint64(j.Get(pattern))
|
||||
}
|
||||
|
||||
func (j *Json) GetFloat32(pattern string) float32 {
|
||||
return gconv.Float32(j.Get(pattern))
|
||||
}
|
||||
|
||||
func (j *Json) GetFloat64(pattern string) float64 {
|
||||
return gconv.Float64(j.Get(pattern))
|
||||
}
|
||||
|
||||
func (j *Json) GetFloats(pattern string) []float64 {
|
||||
return gconv.Floats(j.Get(pattern))
|
||||
}
|
||||
|
||||
// GetToStruct gets the value by specified <pattern>,
|
||||
// and converts it to specified object <objPointer>.
|
||||
// The <objPointer> should be the pointer to an object.
|
||||
func (j *Json) GetToStruct(pattern string, objPointer interface{}) error {
|
||||
return gconv.Struct(j.Get(pattern), objPointer)
|
||||
}
|
||||
|
||||
// Set sets value with specified <pattern>.
|
||||
// It supports hierarchical data access by char separator, which is '.' in default.
|
||||
func (j *Json) Set(pattern string, value interface{}) error {
|
||||
return j.setValue(pattern, value, false)
|
||||
}
|
||||
|
||||
// Remove deletes value with specified <pattern>.
|
||||
// It supports hierarchical data access by char separator, which is '.' in default.
|
||||
func (j *Json) Remove(pattern string) error {
|
||||
return j.setValue(pattern, nil, true)
|
||||
}
|
||||
|
||||
// Set <value> by <pattern>.
|
||||
// Notice:
|
||||
// 1. If value is nil and removed is true, means deleting this value;
|
||||
@ -576,69 +242,6 @@ func (j *Json) setPointerWithValue(pointer *interface{}, key string, value inter
|
||||
return pointer
|
||||
}
|
||||
|
||||
// Get returns value by specified <pattern>.
|
||||
// It returns all values of current Json object, if <pattern> is empty or not specified.
|
||||
// It returns nil if no value found by <pattern>.
|
||||
//
|
||||
// We can also access slice item by its index number in <pattern>,
|
||||
// eg: "items.name.first", "list.10".
|
||||
func (j *Json) Get(pattern...string) interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
|
||||
queryPattern := ""
|
||||
if len(pattern) > 0 {
|
||||
queryPattern = pattern[0]
|
||||
}
|
||||
var result *interface{}
|
||||
if j.vc {
|
||||
result = j.getPointerByPattern(queryPattern)
|
||||
} else {
|
||||
result = j.getPointerByPatternWithoutViolenceCheck(queryPattern)
|
||||
}
|
||||
if result != nil {
|
||||
return *result
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Contains checks whether the value by specified <pattern> exist.
|
||||
func (j *Json) Contains(pattern...string) bool {
|
||||
return j.Get(pattern...) != nil
|
||||
}
|
||||
|
||||
// Len returns the length/size of the value by specified <pattern>.
|
||||
// The target value by <pattern> should be type of slice or map.
|
||||
// It returns -1 if the target value is not found, or its type is invalid.
|
||||
func (j *Json) Len(pattern string) int {
|
||||
p := j.getPointerByPattern(pattern)
|
||||
if p != nil {
|
||||
switch (*p).(type) {
|
||||
case map[string]interface{}:
|
||||
return len((*p).(map[string]interface{}))
|
||||
case []interface{}:
|
||||
return len((*p).([]interface{}))
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Append appends value to the value by specified <pattern>.
|
||||
// The target value by <pattern> should be type of slice.
|
||||
func (j *Json) Append(pattern string, value interface{}) error {
|
||||
p := j.getPointerByPattern(pattern)
|
||||
if p == nil {
|
||||
return j.Set(fmt.Sprintf("%s.0", pattern), value)
|
||||
}
|
||||
switch (*p).(type) {
|
||||
case []interface{}:
|
||||
return j.Set(fmt.Sprintf("%s.%d", pattern, len((*p).([]interface{}))), value)
|
||||
}
|
||||
return fmt.Errorf("invalid variable type of %s", pattern)
|
||||
}
|
||||
|
||||
// Get a pointer to the value by specified <pattern>.
|
||||
func (j *Json) getPointerByPattern(pattern string) *interface{} {
|
||||
if j.vc {
|
||||
@ -729,111 +332,3 @@ func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interfac
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToMap converts current Json object to map[string]interface{}.
|
||||
// It returns nil if fails.
|
||||
func (j *Json) ToMap() map[string]interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
switch (*(j.p)).(type) {
|
||||
case map[string]interface{}:
|
||||
return (*(j.p)).(map[string]interface{})
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ToArray converts current Json object to []interface{}.
|
||||
// It returns nil if fails.
|
||||
func (j *Json) ToArray() []interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
switch (*(j.p)).(type) {
|
||||
case []interface{}:
|
||||
return (*(j.p)).([]interface{})
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (j *Json) ToXml(rootTag...string) ([]byte, error) {
|
||||
return gxml.Encode(j.ToMap(), rootTag...)
|
||||
}
|
||||
|
||||
func (j *Json) ToXmlString(rootTag...string) (string, error) {
|
||||
b, e := j.ToXml(rootTag...)
|
||||
return string(b), e
|
||||
}
|
||||
|
||||
func (j *Json) ToXmlIndent(rootTag...string) ([]byte, error) {
|
||||
return gxml.EncodeWithIndent(j.ToMap(), rootTag...)
|
||||
}
|
||||
|
||||
func (j *Json) ToXmlIndentString(rootTag...string) (string, error) {
|
||||
b, e := j.ToXmlIndent(rootTag...)
|
||||
return string(b), e
|
||||
}
|
||||
|
||||
func (j *Json) ToJson() ([]byte, error) {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return Encode(*(j.p))
|
||||
}
|
||||
|
||||
func (j *Json) ToJsonString() (string, error) {
|
||||
b, e := j.ToJson()
|
||||
return string(b), e
|
||||
}
|
||||
|
||||
func (j *Json) ToJsonIndent() ([]byte, error) {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return json.MarshalIndent(*(j.p), "", "\t")
|
||||
}
|
||||
|
||||
func (j *Json) ToJsonIndentString() (string, error) {
|
||||
b, e := j.ToJsonIndent()
|
||||
return string(b), e
|
||||
}
|
||||
|
||||
func (j *Json) ToYaml() ([]byte, error) {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gyaml.Encode(*(j.p))
|
||||
}
|
||||
|
||||
func (j *Json) ToYamlString() (string, error) {
|
||||
b, e := j.ToYaml()
|
||||
return string(b), e
|
||||
}
|
||||
|
||||
func (j *Json) ToToml() ([]byte, error) {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gtoml.Encode(*(j.p))
|
||||
}
|
||||
|
||||
func (j *Json) ToTomlString() (string, error) {
|
||||
b, e := j.ToToml()
|
||||
return string(b), e
|
||||
}
|
||||
|
||||
// ToStruct converts current Json object to specified object.
|
||||
// The <objPointer> should be a pointer type.
|
||||
func (j *Json) ToStruct(objPointer interface{}) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Struct(*(j.p), objPointer)
|
||||
}
|
||||
|
||||
// Dump prints current Json object with more manually readable.
|
||||
func (j *Json) Dump() error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
if b, err := j.ToJsonIndent(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
296
g/encoding/gjson/gjson_api.go
Normal file
296
g/encoding/gjson/gjson_api.go
Normal file
@ -0,0 +1,296 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gjson
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Val returns the json value.
|
||||
func (j *Json) Value() interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return *(j.p)
|
||||
}
|
||||
|
||||
// Get returns value by specified <pattern>.
|
||||
// It returns all values of current Json object, if <pattern> is empty or not specified.
|
||||
// It returns nil if no value found by <pattern>.
|
||||
//
|
||||
// We can also access slice item by its index number in <pattern>,
|
||||
// eg: "items.name.first", "list.10".
|
||||
//
|
||||
// It returns a default value specified by <def> if value for <pattern> is not found.
|
||||
func (j *Json) Get(pattern string, def...interface{}) interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
|
||||
var result *interface{}
|
||||
if j.vc {
|
||||
result = j.getPointerByPattern(pattern)
|
||||
} else {
|
||||
result = j.getPointerByPatternWithoutViolenceCheck(pattern)
|
||||
}
|
||||
if result != nil {
|
||||
return *result
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetVar returns a *gvar.Var with value by given <pattern>.
|
||||
func (j *Json) GetVar(pattern string, def...interface{}) *gvar.Var {
|
||||
return gvar.New(j.Get(pattern, def...), true)
|
||||
}
|
||||
|
||||
// GetMap gets the value by specified <pattern>,
|
||||
// and converts it to map[string]interface{}.
|
||||
func (j *Json) GetMap(pattern string, def...interface{}) map[string]interface{} {
|
||||
result := j.Get(pattern, def...)
|
||||
if result != nil {
|
||||
return gconv.Map(result)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetJson gets the value by specified <pattern>,
|
||||
// and converts it to a Json object.
|
||||
func (j *Json) GetJson(pattern string, def...interface{}) *Json {
|
||||
result := j.Get(pattern, def...)
|
||||
if result != nil {
|
||||
return New(result)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetJsons gets the value by specified <pattern>,
|
||||
// and converts it to a slice of Json object.
|
||||
func (j *Json) GetJsons(pattern string, def...interface{}) []*Json {
|
||||
array := j.GetArray(pattern, def...)
|
||||
if len(array) > 0 {
|
||||
jsons := make([]*Json, len(array))
|
||||
for i := 0; i < len(array); i++ {
|
||||
jsons[i] = New(array[i], !j.mu.IsSafe())
|
||||
}
|
||||
return jsons
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetArray gets the value by specified <pattern>,
|
||||
// and converts it to a slice of []interface{}.
|
||||
func (j *Json) GetArray(pattern string, def...interface{}) []interface{} {
|
||||
return gconv.Interfaces(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
// GetString gets the value by specified <pattern>,
|
||||
// and converts it to string.
|
||||
func (j *Json) GetString(pattern string, def...interface{}) string {
|
||||
return gconv.String(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
// GetBool gets the value by specified <pattern>,
|
||||
// and converts it to bool.
|
||||
// It returns false when value is: "", 0, false, off, nil;
|
||||
// or returns true instead.
|
||||
func (j *Json) GetBool(pattern string, def...interface{}) bool {
|
||||
return gconv.Bool(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetInt(pattern string, def...interface{}) int {
|
||||
return gconv.Int(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetInt8(pattern string, def...interface{}) int8 {
|
||||
return gconv.Int8(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetInt16(pattern string, def...interface{}) int16 {
|
||||
return gconv.Int16(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetInt32(pattern string, def...interface{}) int32 {
|
||||
return gconv.Int32(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetInt64(pattern string, def...interface{}) int64 {
|
||||
return gconv.Int64(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetUint(pattern string, def...interface{}) uint {
|
||||
return gconv.Uint(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetUint8(pattern string, def...interface{}) uint8 {
|
||||
return gconv.Uint8(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetUint16(pattern string, def...interface{}) uint16 {
|
||||
return gconv.Uint16(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetUint32(pattern string, def...interface{}) uint32 {
|
||||
return gconv.Uint32(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetUint64(pattern string, def...interface{}) uint64 {
|
||||
return gconv.Uint64(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetFloat32(pattern string, def...interface{}) float32 {
|
||||
return gconv.Float32(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetFloat64(pattern string, def...interface{}) float64 {
|
||||
return gconv.Float64(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetFloats(pattern string, def...interface{}) []float64 {
|
||||
return gconv.Floats(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetInts(pattern string, def...interface{}) []int {
|
||||
return gconv.Ints(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
// GetStrings gets the value by specified <pattern>,
|
||||
// and converts it to a slice of []string.
|
||||
func (j *Json) GetStrings(pattern string, def...interface{}) []string {
|
||||
return gconv.Strings(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
// See GetArray.
|
||||
func (j *Json) GetInterfaces(pattern string, def...interface{}) []interface{} {
|
||||
return gconv.Interfaces(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetTime(pattern string, format... string) time.Time {
|
||||
return gconv.Time(j.Get(pattern), format...)
|
||||
}
|
||||
|
||||
func (j *Json) GetDuration(pattern string, def...interface{}) time.Duration {
|
||||
return gconv.Duration(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetGTime(pattern string, format... string) *gtime.Time {
|
||||
return gconv.GTime(j.Get(pattern), format...)
|
||||
}
|
||||
|
||||
// Set sets value with specified <pattern>.
|
||||
// It supports hierarchical data access by char separator, which is '.' in default.
|
||||
func (j *Json) Set(pattern string, value interface{}) error {
|
||||
return j.setValue(pattern, value, false)
|
||||
}
|
||||
|
||||
// Remove deletes value with specified <pattern>.
|
||||
// It supports hierarchical data access by char separator, which is '.' in default.
|
||||
func (j *Json) Remove(pattern string) error {
|
||||
return j.setValue(pattern, nil, true)
|
||||
}
|
||||
|
||||
// Contains checks whether the value by specified <pattern> exist.
|
||||
func (j *Json) Contains(pattern string) bool {
|
||||
return j.Get(pattern) != nil
|
||||
}
|
||||
|
||||
// Len returns the length/size of the value by specified <pattern>.
|
||||
// The target value by <pattern> should be type of slice or map.
|
||||
// It returns -1 if the target value is not found, or its type is invalid.
|
||||
func (j *Json) Len(pattern string) int {
|
||||
p := j.getPointerByPattern(pattern)
|
||||
if p != nil {
|
||||
switch (*p).(type) {
|
||||
case map[string]interface{}:
|
||||
return len((*p).(map[string]interface{}))
|
||||
case []interface{}:
|
||||
return len((*p).([]interface{}))
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Append appends value to the value by specified <pattern>.
|
||||
// The target value by <pattern> should be type of slice.
|
||||
func (j *Json) Append(pattern string, value interface{}) error {
|
||||
p := j.getPointerByPattern(pattern)
|
||||
if p == nil {
|
||||
return j.Set(fmt.Sprintf("%s.0", pattern), value)
|
||||
}
|
||||
switch (*p).(type) {
|
||||
case []interface{}:
|
||||
return j.Set(fmt.Sprintf("%s.%d", pattern, len((*p).([]interface{}))), value)
|
||||
}
|
||||
return fmt.Errorf("invalid variable type of %s", pattern)
|
||||
}
|
||||
|
||||
// GetToVar gets the value by specified <pattern>,
|
||||
// and converts it to specified golang variable <v>.
|
||||
// The <pointer> should be a pointer type.
|
||||
func (j *Json) GetToVar(pattern string, pointer interface{}) error {
|
||||
r := j.Get(pattern)
|
||||
if r != nil {
|
||||
if t, err := Encode(r); err == nil {
|
||||
return DecodeTo(t, pointer)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
pointer = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetToStruct gets the value by specified <pattern>,
|
||||
// and converts it to specified object <objPointer>.
|
||||
// The <objPointer> should be the pointer to an object.
|
||||
func (j *Json) GetToStruct(pattern string, pointer interface{}) error {
|
||||
return gconv.Struct(j.Get(pattern), pointer)
|
||||
}
|
||||
|
||||
// ToMap converts current Json object to map[string]interface{}.
|
||||
// It returns nil if fails.
|
||||
func (j *Json) ToMap() map[string]interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Map(*(j.p))
|
||||
}
|
||||
|
||||
// ToArray converts current Json object to []interface{}.
|
||||
// It returns nil if fails.
|
||||
func (j *Json) ToArray() []interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Interfaces(*(j.p))
|
||||
}
|
||||
|
||||
// ToStruct converts current Json object to specified object.
|
||||
// The <objPointer> should be a pointer type.
|
||||
func (j *Json) ToStruct(pointer interface{}) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Struct(*(j.p), pointer)
|
||||
}
|
||||
|
||||
// Dump prints current Json object with more manually readable.
|
||||
func (j *Json) Dump() error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
if b, err := j.ToJsonIndent(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
21
g/encoding/gjson/gjson_api_config.go
Normal file
21
g/encoding/gjson/gjson_api_config.go
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gjson
|
||||
|
||||
// SetSplitChar sets the separator char for hierarchical data access.
|
||||
func (j *Json) SetSplitChar(char byte) {
|
||||
j.mu.Lock()
|
||||
j.c = char
|
||||
j.mu.Unlock()
|
||||
}
|
||||
|
||||
// SetViolenceCheck enables/disables violence check for hierarchical data access.
|
||||
func (j *Json) SetViolenceCheck(enabled bool) {
|
||||
j.mu.Lock()
|
||||
j.vc = enabled
|
||||
j.mu.Unlock()
|
||||
}
|
||||
76
g/encoding/gjson/gjson_api_encoding.go
Normal file
76
g/encoding/gjson/gjson_api_encoding.go
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gjson
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/g/encoding/gtoml"
|
||||
"github.com/gogf/gf/g/encoding/gxml"
|
||||
"github.com/gogf/gf/g/encoding/gyaml"
|
||||
)
|
||||
|
||||
func (j *Json) ToXml(rootTag...string) ([]byte, error) {
|
||||
return gxml.Encode(j.ToMap(), rootTag...)
|
||||
}
|
||||
|
||||
func (j *Json) ToXmlString(rootTag...string) (string, error) {
|
||||
b, e := j.ToXml(rootTag...)
|
||||
return string(b), e
|
||||
}
|
||||
|
||||
func (j *Json) ToXmlIndent(rootTag...string) ([]byte, error) {
|
||||
return gxml.EncodeWithIndent(j.ToMap(), rootTag...)
|
||||
}
|
||||
|
||||
func (j *Json) ToXmlIndentString(rootTag...string) (string, error) {
|
||||
b, e := j.ToXmlIndent(rootTag...)
|
||||
return string(b), e
|
||||
}
|
||||
|
||||
func (j *Json) ToJson() ([]byte, error) {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return Encode(*(j.p))
|
||||
}
|
||||
|
||||
func (j *Json) ToJsonString() (string, error) {
|
||||
b, e := j.ToJson()
|
||||
return string(b), e
|
||||
}
|
||||
|
||||
func (j *Json) ToJsonIndent() ([]byte, error) {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return json.MarshalIndent(*(j.p), "", "\t")
|
||||
}
|
||||
|
||||
func (j *Json) ToJsonIndentString() (string, error) {
|
||||
b, e := j.ToJsonIndent()
|
||||
return string(b), e
|
||||
}
|
||||
|
||||
func (j *Json) ToYaml() ([]byte, error) {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gyaml.Encode(*(j.p))
|
||||
}
|
||||
|
||||
func (j *Json) ToYamlString() (string, error) {
|
||||
b, e := j.ToYaml()
|
||||
return string(b), e
|
||||
}
|
||||
|
||||
func (j *Json) ToToml() ([]byte, error) {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gtoml.Encode(*(j.p))
|
||||
}
|
||||
|
||||
func (j *Json) ToTomlString() (string, error) {
|
||||
b, e := j.ToToml()
|
||||
return string(b), e
|
||||
}
|
||||
182
g/encoding/gjson/gjson_api_new_load.go
Normal file
182
g/encoding/gjson/gjson_api_new_load.go
Normal file
@ -0,0 +1,182 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gjson provides convenient API for JSON/XML/YAML/TOML data handling.
|
||||
package gjson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/encoding/gtoml"
|
||||
"github.com/gogf/gf/g/encoding/gxml"
|
||||
"github.com/gogf/gf/g/encoding/gyaml"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/os/gfcache"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// New creates a Json object with any variable type of <data>,
|
||||
// but <data> should be a map or slice for data access reason,
|
||||
// or it will make no sense.
|
||||
// The <unsafe> param specifies whether using this Json object
|
||||
// in un-concurrent-safe context, which is false in default.
|
||||
func New(data interface{}, unsafe...bool) *Json {
|
||||
j := (*Json)(nil)
|
||||
switch data.(type) {
|
||||
case string, []byte:
|
||||
if r, err := LoadContent(gconv.Bytes(data)); err == nil {
|
||||
j = r
|
||||
} else {
|
||||
j = &Json {
|
||||
p : &data,
|
||||
c : byte(gDEFAULT_SPLIT_CHAR),
|
||||
vc : false ,
|
||||
}
|
||||
}
|
||||
default:
|
||||
rv := reflect.ValueOf(data)
|
||||
kind := rv.Kind()
|
||||
switch kind {
|
||||
case reflect.Slice: fallthrough
|
||||
case reflect.Array:
|
||||
i := interface{}(nil)
|
||||
i = gconv.Interfaces(data)
|
||||
j = &Json {
|
||||
p : &i,
|
||||
c : byte(gDEFAULT_SPLIT_CHAR),
|
||||
vc : false ,
|
||||
}
|
||||
case reflect.Map: fallthrough
|
||||
case reflect.Struct:
|
||||
i := interface{}(nil)
|
||||
i = gconv.Map(data)
|
||||
j = &Json {
|
||||
p : &i,
|
||||
c : byte(gDEFAULT_SPLIT_CHAR),
|
||||
vc : false ,
|
||||
}
|
||||
default:
|
||||
j = &Json {
|
||||
p : &data,
|
||||
c : byte(gDEFAULT_SPLIT_CHAR),
|
||||
vc : false ,
|
||||
}
|
||||
}
|
||||
}
|
||||
j.mu = rwmutex.New(unsafe...)
|
||||
return j
|
||||
}
|
||||
|
||||
// NewUnsafe creates a un-concurrent-safe Json object.
|
||||
func NewUnsafe(data...interface{}) *Json {
|
||||
if len(data) > 0 {
|
||||
return New(data[0], true)
|
||||
}
|
||||
return New(nil, true)
|
||||
}
|
||||
|
||||
// Valid checks whether <data> is a valid JSON data type.
|
||||
func Valid(data interface{}) bool {
|
||||
return json.Valid(gconv.Bytes(data))
|
||||
}
|
||||
|
||||
// Encode encodes <value> to JSON data type of bytes.
|
||||
func Encode(value interface{}) ([]byte, error) {
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
// Decode decodes <data>(string/[]byte) to golang variable.
|
||||
func Decode(data interface{}) (interface{}, error) {
|
||||
var value interface{}
|
||||
if err := DecodeTo(gconv.Bytes(data), &value); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Decode decodes <data>(string/[]byte) to specified golang variable <v>.
|
||||
// The <v> should be a pointer type.
|
||||
func DecodeTo(data interface{}, v interface{}) error {
|
||||
decoder := json.NewDecoder(bytes.NewReader(gconv.Bytes(data)))
|
||||
decoder.UseNumber()
|
||||
return decoder.Decode(v)
|
||||
}
|
||||
|
||||
// DecodeToJson codes <data>(string/[]byte) to a Json object.
|
||||
func DecodeToJson(data interface{}, unsafe...bool) (*Json, error) {
|
||||
if v, err := Decode(gconv.Bytes(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return New(v, unsafe...), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Load loads content from specified file <path>,
|
||||
// and creates a Json object from its content.
|
||||
func Load(path string, unsafe...bool) (*Json, error) {
|
||||
return LoadContent(gfcache.GetBinContents(path), unsafe...)
|
||||
}
|
||||
|
||||
// LoadContent creates a Json object from given content,
|
||||
// it checks the data type of <content> automatically,
|
||||
// supporting JSON, XML, YAML and TOML types of data.
|
||||
func LoadContent(data interface{}, unsafe...bool) (*Json, error) {
|
||||
var err error
|
||||
var result interface{}
|
||||
b := gconv.Bytes(data)
|
||||
t := "json"
|
||||
// auto check data type
|
||||
if json.Valid(b) {
|
||||
t = "json"
|
||||
} else if gregex.IsMatch(`^<.+>.*</.+>$`, b) {
|
||||
t = "xml"
|
||||
} else if gregex.IsMatch(`^[\s\t]*\w+\s*:\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*:\s*.+`, b) {
|
||||
t = "yml"
|
||||
} else if gregex.IsMatch(`^[\s\t]*\w+\s*=\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*=\s*.+`, b) {
|
||||
t = "toml"
|
||||
} else {
|
||||
return nil, errors.New("unsupported data type")
|
||||
}
|
||||
// convert to json type data
|
||||
switch t {
|
||||
case "json", ".json":
|
||||
// ok
|
||||
case "xml", ".xml":
|
||||
// TODO UseNumber
|
||||
b, err = gxml.ToJson(b)
|
||||
|
||||
case "yml", "yaml", ".yml", ".yaml":
|
||||
// TODO UseNumber
|
||||
b, err = gyaml.ToJson(b)
|
||||
|
||||
case "toml", ".toml":
|
||||
// TODO UseNumber
|
||||
b, err = gtoml.ToJson(b)
|
||||
|
||||
default:
|
||||
err = errors.New("nonsupport type " + t)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result == nil {
|
||||
decoder := json.NewDecoder(bytes.NewReader(b))
|
||||
decoder.UseNumber()
|
||||
if err := decoder.Decode(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch result.(type) {
|
||||
case string, []byte:
|
||||
return nil, fmt.Errorf(`json decoding failed for content: %s`, string(b))
|
||||
}
|
||||
}
|
||||
return New(result, unsafe...), nil
|
||||
}
|
||||
63
g/encoding/gjson/gjson_func.go
Normal file
63
g/encoding/gjson/gjson_func.go
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 gjson
|
||||
|
||||
//func MarshalOrdered(value interface{}) ([]byte, error) {
|
||||
// buffer := bytes.NewBuffer(nil)
|
||||
// rv := reflect.ValueOf(value)
|
||||
// kind := rv.Kind()
|
||||
// if kind == reflect.Ptr {
|
||||
// rv = rv.Elem()
|
||||
// kind = rv.Kind()
|
||||
// }
|
||||
// switch kind {
|
||||
// case reflect.Slice: fallthrough
|
||||
// case reflect.Array:
|
||||
// buffer.WriteByte('[')
|
||||
// length := rv.Len()
|
||||
// for i := 0; i < length; i++ {
|
||||
// if p, err := MarshalOrdered(rv.Index(i).Interface()); err != nil {
|
||||
// return nil, err
|
||||
// } else {
|
||||
// buffer.Write(p)
|
||||
// if i < length - 1 {
|
||||
// buffer.WriteByte(',')
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// buffer.WriteByte(']')
|
||||
// case reflect.Map: fallthrough
|
||||
// case reflect.Struct:
|
||||
// m := gconv.Map(value, "json")
|
||||
// keys := make([]string, len(m))
|
||||
// index := 0
|
||||
// for key := range m {
|
||||
// keys[index] = key
|
||||
// index++
|
||||
// }
|
||||
// sort.Strings(keys)
|
||||
// buffer.WriteByte('{')
|
||||
// for i, key := range keys {
|
||||
// if p, err := MarshalOrdered(m[key]); err != nil {
|
||||
// return nil, err
|
||||
// } else {
|
||||
// buffer.WriteString(fmt.Sprintf(`"%s":%s`, key, string(p)))
|
||||
// if i < index - 1 {
|
||||
// buffer.WriteByte(',')
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// buffer.WriteByte('}')
|
||||
// default:
|
||||
// if p, err := json.Marshal(value); err != nil {
|
||||
// return nil, err
|
||||
// } else {
|
||||
// buffer.Write(p)
|
||||
// }
|
||||
// }
|
||||
// return buffer.Bytes(), nil
|
||||
//}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user