From ea9cba66c0c6425f35e14671eefd902e49c2faea Mon Sep 17 00:00:00 2001 From: shanyujie <1196661499@qq.com> Date: Wed, 14 Jan 2026 12:02:19 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat(container):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89nil=E6=A3=80=E6=9F=A5=E5=99=A8?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E6=94=AF=E6=8C=81,=E8=A7=A3=E5=86=B3typed=20?= =?UTF-8?q?nil=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在KVMap、ListKVMap、TSet和各种树结构中引入NilChecker类型 - 实现RegisterNilChecker方法用于注册自定义nil检查函数 - 添加isNil方法统一处理nil值判断逻辑 - 修改GetOrSet、AddIfNotExist等相关方法使用新的nil检查机制 - 为所有支持的数据结构添加相应的单元测试验证功能正确性 - 更新红黑树比较器调用方式以支持泛型参数传递 --- container/gmap/gmap_hash_k_v_map.go | 33 ++++- container/gmap/gmap_list_k_v_map.go | 37 ++++-- container/gmap/gmap_z_unit_k_v_map_test.go | 32 +++++ .../gmap/gmap_z_unit_list_k_v_map_test.go | 32 +++++ container/gset/gset_t_set.go | 32 ++++- container/gset/gset_z_unit_t_set_test.go | 20 +++ container/gtree/gtree_k_v_avltree.go | 26 +++- container/gtree/gtree_k_v_btree.go | 23 +++- container/gtree/gtree_k_v_redblacktree.go | 23 +++- container/gtree/gtree_redblacktree.go | 2 +- container/gtree/gtree_z_k_v_tree_test.go | 114 ++++++++++++++++++ 11 files changed, 352 insertions(+), 22 deletions(-) create mode 100644 container/gtree/gtree_z_k_v_tree_test.go diff --git a/container/gmap/gmap_hash_k_v_map.go b/container/gmap/gmap_hash_k_v_map.go index 0b9f9c8ea02..704e6629889 100644 --- a/container/gmap/gmap_hash_k_v_map.go +++ b/container/gmap/gmap_hash_k_v_map.go @@ -17,10 +17,14 @@ import ( "github.com/gogf/gf/v2/util/gconv" ) +// NilChecker is a function that checks whether the given value is nil. +type NilChecker[V any] func(V) bool + // KVMap wraps map type `map[K]V` and provides more map features. type KVMap[K comparable, V any] struct { - mu rwmutex.RWMutex - data map[K]V + mu rwmutex.RWMutex + data map[K]V + nilChecker NilChecker[V] } // NewKVMap creates and returns an empty hash map. @@ -41,6 +45,26 @@ func NewKVMapFrom[K comparable, V any](data map[K]V, safe ...bool) *KVMap[K, V] return m } +// RegisterNilChecker registers a custom nil checker function for the map values. +// This function is used to determine if a value should be considered as nil. +// The nil checker function takes a value of type V and returns a boolean indicating +// whether the value should be treated as nil. +func (m *KVMap[K, V]) RegisterNilChecker(nilChecker NilChecker[V]) { + m.mu.Lock() + defer m.mu.Unlock() + m.nilChecker = nilChecker +} + +// isNil checks whether the given value is nil. +// It first checks if a custom nil checker function is registered and uses it if available, +// otherwise it performs a standard nil check using any(v) == nil. +func (m *KVMap[K, V]) isNil(v V) bool { + if m.nilChecker != nil { + return m.nilChecker(v) + } + return any(v) == nil +} + // Iterator iterates the hash map readonly with custom callback function `f`. // If `f` returns true, then it continues iterating; or false to stop. func (m *KVMap[K, V]) Iterator(f func(k K, v V) bool) { @@ -217,8 +241,7 @@ func (m *KVMap[K, V]) doSetWithLockCheck(key K, value V) (val V, ok bool) { if v, ok := m.data[key]; ok { return v, true } - - if any(value) != nil { + if !m.isNil(value) { m.data[key] = value } return value, false @@ -255,7 +278,7 @@ func (m *KVMap[K, V]) GetOrSetFuncLock(key K, f func() V) V { return v } value := f() - if any(value) != nil { + if !m.isNil(value) { m.data[key] = value } return value diff --git a/container/gmap/gmap_list_k_v_map.go b/container/gmap/gmap_list_k_v_map.go index 6bfe2a7e95b..c23bf262be3 100644 --- a/container/gmap/gmap_list_k_v_map.go +++ b/container/gmap/gmap_list_k_v_map.go @@ -27,9 +27,10 @@ import ( // // Reference: http://en.wikipedia.org/wiki/Associative_array type ListKVMap[K comparable, V any] struct { - mu rwmutex.RWMutex - data map[K]*glist.TElement[*gListKVMapNode[K, V]] - list *glist.TList[*gListKVMapNode[K, V]] + mu rwmutex.RWMutex + data map[K]*glist.TElement[*gListKVMapNode[K, V]] + list *glist.TList[*gListKVMapNode[K, V]] + nilChecker NilChecker[V] } type gListKVMapNode[K comparable, V any] struct { @@ -58,6 +59,26 @@ func NewListKVMapFrom[K comparable, V any](data map[K]V, safe ...bool) *ListKVMa return m } +// RegisterNilChecker registers a custom nil checker function for the map values. +// This function is used to determine if a value should be considered as nil. +// The nil checker function takes a value of type V and returns a boolean indicating +// whether the value should be treated as nil. +func (m *ListKVMap[K, V]) RegisterNilChecker(nilChecker NilChecker[V]) { + m.mu.Lock() + defer m.mu.Unlock() + m.nilChecker = nilChecker +} + +// isNil checks whether the given value is nil. +// It first checks if a custom nil checker function is registered and uses it if available, +// otherwise it performs a standard nil check using any(v) == nil. +func (m *ListKVMap[K, V]) isNil(v V) bool { + if m.nilChecker != nil { + return m.nilChecker(v) + } + return any(v) == nil +} + // Iterator is alias of IteratorAsc. func (m *ListKVMap[K, V]) Iterator(f func(key K, value V) bool) { m.IteratorAsc(f) @@ -282,7 +303,7 @@ func (m *ListKVMap[K, V]) doSetWithLockCheckWithoutLock(key K, value V) V { if e, ok := m.data[key]; ok { return e.Value.value } - if any(value) != nil { + if !m.isNil(value) { m.data[key] = m.list.PushBack(&gListKVMapNode[K, V]{key, value}) } return value @@ -327,7 +348,7 @@ func (m *ListKVMap[K, V]) GetOrSetFuncLock(key K, f func() V) V { return e.Value.value } value := f() - if any(value) != nil { + if !m.isNil(value) { m.data[key] = m.list.PushBack(&gListKVMapNode[K, V]{key, value}) } return value @@ -370,7 +391,7 @@ func (m *ListKVMap[K, V]) SetIfNotExist(key K, value V) bool { if _, ok := m.data[key]; ok { return false } - if any(value) != nil { + if !m.isNil(value) { m.data[key] = m.list.PushBack(&gListKVMapNode[K, V]{key, value}) } return true @@ -390,7 +411,7 @@ func (m *ListKVMap[K, V]) SetIfNotExistFunc(key K, f func() V) bool { return false } value := f() - if any(value) != nil { + if !m.isNil(value) { m.data[key] = m.list.PushBack(&gListKVMapNode[K, V]{key, value}) } return true @@ -413,7 +434,7 @@ func (m *ListKVMap[K, V]) SetIfNotExistFuncLock(key K, f func() V) bool { return false } value := f() - if any(value) != nil { + if !m.isNil(value) { m.data[key] = m.list.PushBack(&gListKVMapNode[K, V]{key, value}) } return true diff --git a/container/gmap/gmap_z_unit_k_v_map_test.go b/container/gmap/gmap_z_unit_k_v_map_test.go index faef2c31651..259f0d02607 100644 --- a/container/gmap/gmap_z_unit_k_v_map_test.go +++ b/container/gmap/gmap_z_unit_k_v_map_test.go @@ -1630,3 +1630,35 @@ func Test_KVMap_Flip_String(t *testing.T) { t.Assert(m.Get("val2"), "key2") }) } + +func Test_KVMap_TypedNil(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Student struct { + Name string + Age int + } + m1 := gmap.NewKVMap[int, *Student](true) + for i := 0; i < 10; i++ { + m1.GetOrSetFuncLock(i, func() *Student { + if i%2 == 0 { + return &Student{} + } + return nil + }) + } + t.Assert(m1.Size(), 10) + m2 := gmap.NewKVMap[int, *Student](true) + m2.RegisterNilChecker(func(student *Student) bool { + return student == nil + }) + for i := 0; i < 10; i++ { + m2.GetOrSetFuncLock(i, func() *Student { + if i%2 == 0 { + return &Student{} + } + return nil + }) + } + t.Assert(m2.Size(), 5) + }) +} diff --git a/container/gmap/gmap_z_unit_list_k_v_map_test.go b/container/gmap/gmap_z_unit_list_k_v_map_test.go index 7714f532f92..194e52c961f 100644 --- a/container/gmap/gmap_z_unit_list_k_v_map_test.go +++ b/container/gmap/gmap_z_unit_list_k_v_map_test.go @@ -1341,3 +1341,35 @@ func Test_ListKVMap_UnmarshalValue_NilData(t *testing.T) { t.Assert(m.Get("b"), "2") }) } + +func Test_ListKVMap_TypedNil(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Student struct { + Name string + Age int + } + m1 := gmap.NewListKVMap[int, *Student](true) + for i := 0; i < 10; i++ { + m1.GetOrSetFuncLock(i, func() *Student { + if i%2 == 0 { + return &Student{} + } + return nil + }) + } + t.Assert(m1.Size(), 10) + m2 := gmap.NewListKVMap[int, *Student](true) + m2.RegisterNilChecker(func(student *Student) bool { + return student == nil + }) + for i := 0; i < 10; i++ { + m2.GetOrSetFuncLock(i, func() *Student { + if i%2 == 0 { + return &Student{} + } + return nil + }) + } + t.Assert(m2.Size(), 5) + }) +} diff --git a/container/gset/gset_t_set.go b/container/gset/gset_t_set.go index ae3507ceca4..25dcdda59e2 100644 --- a/container/gset/gset_t_set.go +++ b/container/gset/gset_t_set.go @@ -15,10 +15,14 @@ import ( "github.com/gogf/gf/v2/util/gconv" ) +// NilChecker is a function that checks whether the given value is nil. +type NilChecker[T any] func(T) bool + // TSet[T] is consisted of any items. type TSet[T comparable] struct { - mu rwmutex.RWMutex - data map[T]struct{} + mu rwmutex.RWMutex + data map[T]struct{} + nilChecker NilChecker[T] } // NewTSet creates and returns a new set, which contains un-repeated items. @@ -43,6 +47,24 @@ func NewTSetFrom[T comparable](items []T, safe ...bool) *TSet[T] { } } +// RegisterNilChecker registers a custom nil checker function for the set elements. +// This function is used to determine if an element should be considered as nil. +// The nil checker function takes an element of type T and returns a boolean indicating +// whether the element should be treated as nil. +func (set *TSet[T]) RegisterNilChecker(nilChecker NilChecker[T]) { + set.nilChecker = nilChecker +} + +// isNil checks whether the given value is nil. +// It first checks if a custom nil checker function is registered and uses it if available, +// otherwise it performs a standard nil check using any(v) == nil. +func (set *TSet[T]) isNil(v T) bool { + if set.nilChecker != nil { + return set.nilChecker(v) + } + return any(v) == nil +} + // Iterator iterates the set readonly with given callback function `f`, // if `f` returns true then continue iterating; or false to stop. func (set *TSet[T]) Iterator(f func(v T) bool) { @@ -71,7 +93,7 @@ func (set *TSet[T]) Add(items ...T) { // // Note that, if `item` is nil, it does nothing and returns false. func (set *TSet[T]) AddIfNotExist(item T) bool { - if any(item) == nil { + if set.isNil(item) { return false } if !set.Contains(item) { @@ -95,7 +117,7 @@ func (set *TSet[T]) AddIfNotExist(item T) bool { // Note that, if `item` is nil, it does nothing and returns false. The function `f` // is executed without writing lock. func (set *TSet[T]) AddIfNotExistFunc(item T, f func() bool) bool { - if any(item) == nil { + if set.isNil(item) { return false } if !set.Contains(item) { @@ -121,7 +143,7 @@ func (set *TSet[T]) AddIfNotExistFunc(item T, f func() bool) bool { // Note that, if `item` is nil, it does nothing and returns false. The function `f` // is executed within writing lock. func (set *TSet[T]) AddIfNotExistFuncLock(item T, f func() bool) bool { - if any(item) == nil { + if set.isNil(item) { return false } if !set.Contains(item) { diff --git a/container/gset/gset_z_unit_t_set_test.go b/container/gset/gset_z_unit_t_set_test.go index 4db85fb136d..f97a85e6b7f 100644 --- a/container/gset/gset_z_unit_t_set_test.go +++ b/container/gset/gset_z_unit_t_set_test.go @@ -591,3 +591,23 @@ func TestTSet_RLockFunc(t *testing.T) { t.Assert(sum, 6) }) } + +func Test_TSet_TypedNil(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Student struct { + Name string + Age int + } + set := gset.NewTSet[*Student](true) + var s *Student = nil + exist := set.AddIfNotExist(s) + t.Assert(exist, true) + + set2 := gset.NewTSet[*Student](true) + set2.RegisterNilChecker(func(student *Student) bool { + return student == nil + }) + exist2 := set2.AddIfNotExist(s) + t.Assert(exist2, false) + }) +} diff --git a/container/gtree/gtree_k_v_avltree.go b/container/gtree/gtree_k_v_avltree.go index 323097039bd..afa7f8e9fed 100644 --- a/container/gtree/gtree_k_v_avltree.go +++ b/container/gtree/gtree_k_v_avltree.go @@ -18,11 +18,15 @@ import ( "github.com/gogf/gf/v2/util/gconv" ) +// NilChecker is a function that checks whether the given value is nil. +type NilChecker[V any] func(V) bool + // AVLKVTree holds elements of the AVL tree. type AVLKVTree[K comparable, V any] struct { mu rwmutex.RWMutex comparator func(v1, v2 K) int tree *avltree.Tree[K, V] + nilChecker NilChecker[V] } // AVLKVTreeNode is a single element within the tree. @@ -54,6 +58,26 @@ func NewAVLKVTreeFrom[K comparable, V any](comparator func(v1, v2 K) int, data m return tree } +// RegisterNilChecker registers a custom nil checker function for the map values. +// This function is used to determine if a value should be considered as nil. +// The nil checker function takes a value of type V and returns a boolean indicating +// whether the value should be treated as nil. +func (tree *AVLKVTree[K, V]) RegisterNilChecker(nilChecker NilChecker[V]) { + tree.mu.Lock() + defer tree.mu.Unlock() + tree.nilChecker = nilChecker +} + +// isNil checks whether the given value is nil. +// It first checks if a custom nil checker function is registered and uses it if available, +// otherwise it performs a standard nil check using any(v) == nil. +func (tree *AVLKVTree[K, V]) isNil(value V) bool { + if tree.nilChecker != nil { + return tree.nilChecker(value) + } + return any(value) == nil +} + // Clone clones and returns a new tree from current tree. func (tree *AVLKVTree[K, V]) Clone() *AVLKVTree[K, V] { if tree == nil { @@ -518,7 +542,7 @@ func (tree *AVLKVTree[K, V]) Flip(comparator ...func(v1, v2 K) int) { // // It returns value with given `key`. func (tree *AVLKVTree[K, V]) doSet(key K, value V) V { - if any(value) == nil { + if tree.isNil(value) { return value } tree.tree.Put(key, value) diff --git a/container/gtree/gtree_k_v_btree.go b/container/gtree/gtree_k_v_btree.go index e1a4399ecd6..f76f8d80622 100644 --- a/container/gtree/gtree_k_v_btree.go +++ b/container/gtree/gtree_k_v_btree.go @@ -24,6 +24,7 @@ type BKVTree[K comparable, V any] struct { comparator func(v1, v2 K) int m int // order (maximum number of children) tree *btree.Tree[K, V] + nilChecker NilChecker[V] } // BKVTreeEntry represents the key-value pair contained within nodes. @@ -56,6 +57,26 @@ func NewBKVTreeFrom[K comparable, V any](m int, comparator func(v1, v2 K) int, d return tree } +// RegisterNilChecker registers a custom nil checker function for the map values. +// This function is used to determine if a value should be considered as nil. +// The nil checker function takes a value of type V and returns a boolean indicating +// whether the value should be treated as nil. +func (tree *BKVTree[K, V]) RegisterNilChecker(nilChecker NilChecker[V]) { + tree.mu.Lock() + defer tree.mu.Unlock() + tree.nilChecker = nilChecker +} + +// isNil checks whether the given value is nil. +// It first checks if a custom nil checker function is registered and uses it if available, +// otherwise it performs a standard nil check using any(v) == nil. +func (tree *BKVTree[K, V]) isNil(value V) bool { + if tree.nilChecker != nil { + return tree.nilChecker(value) + } + return any(value) == nil +} + // Clone clones and returns a new tree from current tree. func (tree *BKVTree[K, V]) Clone() *BKVTree[K, V] { if tree == nil { @@ -453,7 +474,7 @@ func (tree *BKVTree[K, V]) Right() *BKVTreeEntry[K, V] { // // It returns value with given `key`. func (tree *BKVTree[K, V]) doSet(key K, value V) V { - if any(value) == nil { + if tree.isNil(value) { return value } tree.tree.Put(key, value) diff --git a/container/gtree/gtree_k_v_redblacktree.go b/container/gtree/gtree_k_v_redblacktree.go index 5b0be020be0..1b3eae13199 100644 --- a/container/gtree/gtree_k_v_redblacktree.go +++ b/container/gtree/gtree_k_v_redblacktree.go @@ -24,6 +24,7 @@ type RedBlackKVTree[K comparable, V any] struct { mu rwmutex.RWMutex comparator func(v1, v2 K) int tree *redblacktree.Tree[K, V] + nilChecker NilChecker[V] } // RedBlackKVTreeNode is a single element within the tree. @@ -75,6 +76,26 @@ func RedBlackKVTreeInitFrom[K comparable, V any](tree *RedBlackKVTree[K, V], com } } +// RegisterNilChecker registers a custom nil checker function for the map values. +// This function is used to determine if a value should be considered as nil. +// The nil checker function takes a value of type V and returns a boolean indicating +// whether the value should be treated as nil. +func (tree *RedBlackKVTree[K, V]) RegisterNilChecker(nilChecker NilChecker[V]) { + tree.mu.Lock() + defer tree.mu.Unlock() + tree.nilChecker = nilChecker +} + +// isNil checks whether the given value is nil. +// It first checks if a custom nil checker function is registered and uses it if available, +// otherwise it performs a standard nil check using any(v) == nil. +func (tree *RedBlackKVTree[K, V]) isNil(value V) bool { + if tree.nilChecker != nil { + return tree.nilChecker(value) + } + return any(value) == nil +} + // SetComparator sets/changes the comparator for sorting. func (tree *RedBlackKVTree[K, V]) SetComparator(comparator func(a, b K) int) { tree.comparator = comparator @@ -592,7 +613,7 @@ func (tree *RedBlackKVTree[K, V]) UnmarshalValue(value any) (err error) { // // It returns value with given `key`. func (tree *RedBlackKVTree[K, V]) doSet(key K, value V) (ret V) { - if any(value) == nil { + if tree.isNil(value) { return } tree.tree.Put(key, value) diff --git a/container/gtree/gtree_redblacktree.go b/container/gtree/gtree_redblacktree.go index 25138b2438d..9735075fbdf 100644 --- a/container/gtree/gtree_redblacktree.go +++ b/container/gtree/gtree_redblacktree.go @@ -46,7 +46,7 @@ func NewRedBlackTreeFrom(comparator func(v1, v2 any) int, data map[any]any, safe func (tree *RedBlackTree) lazyInit() { tree.once.Do(func() { if tree.RedBlackKVTree == nil { - tree.RedBlackKVTree = NewRedBlackKVTree[any, any](gutil.ComparatorTStr, false) + tree.RedBlackKVTree = NewRedBlackKVTree[any, any](gutil.ComparatorTStr[any], false) } }) } diff --git a/container/gtree/gtree_z_k_v_tree_test.go b/container/gtree/gtree_z_k_v_tree_test.go new file mode 100644 index 00000000000..f44b2d97538 --- /dev/null +++ b/container/gtree/gtree_z_k_v_tree_test.go @@ -0,0 +1,114 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtree_test + +import ( + "github.com/gogf/gf/v2/util/gutil" + "testing" + + "github.com/gogf/gf/v2/container/gtree" + "github.com/gogf/gf/v2/test/gtest" +) + +func Test_KVAVLTree_TypedNil(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Student struct { + Name string + Age int + } + avlTree := gtree.NewAVLKVTree[int, *Student](gutil.ComparatorTStr[int], true) + for i := 0; i < 10; i++ { + if i%2 == 0 { + avlTree.Set(i, &Student{}) + } else { + var s *Student = nil + avlTree.Set(i, s) + } + } + t.Assert(avlTree.Size(), 10) + avlTree2 := gtree.NewAVLKVTree[int, *Student](gutil.ComparatorTStr[int], true) + avlTree2.RegisterNilChecker(func(student *Student) bool { + return student == nil + }) + for i := 0; i < 10; i++ { + if i%2 == 0 { + avlTree2.Set(i, &Student{}) + } else { + var s *Student = nil + avlTree2.Set(i, s) + } + } + t.Assert(avlTree2.Size(), 5) + + }) +} + +func Test_KVBTree_TypedNil(t *testing.T) { + type Student struct { + Name string + Age int + } + gtest.C(t, func(t *gtest.T) { + btree := gtree.NewBKVTree[int, *Student](100, gutil.ComparatorTStr[int], true) + for i := 0; i < 10; i++ { + if i%2 == 0 { + btree.Set(i, &Student{}) + } else { + var s *Student = nil + btree.Set(i, s) + } + } + t.Assert(btree.Size(), 10) + btree2 := gtree.NewBKVTree[int, *Student](100, gutil.ComparatorTStr[int], true) + btree2.RegisterNilChecker(func(student *Student) bool { + return student == nil + }) + for i := 0; i < 10; i++ { + if i%2 == 0 { + btree2.Set(i, &Student{}) + } else { + var s *Student = nil + btree2.Set(i, s) + } + } + t.Assert(btree2.Size(), 5) + }) + +} + +func Test_KVRedBlackTree_TypedNil(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Student struct { + Name string + Age int + } + redBlackTree := gtree.NewRedBlackKVTree[int, *Student](gutil.ComparatorTStr[int], true) + for i := 0; i < 10; i++ { + if i%2 == 0 { + redBlackTree.Set(i, &Student{}) + } else { + var s *Student = nil + redBlackTree.Set(i, s) + } + } + t.Assert(redBlackTree.Size(), 10) + redBlackTree2 := gtree.NewRedBlackKVTree[int, *Student](gutil.ComparatorTStr[int], true) + + redBlackTree2.RegisterNilChecker(func(student *Student) bool { + return student == nil + }) + for i := 0; i < 10; i++ { + if i%2 == 0 { + redBlackTree2.Set(i, &Student{}) + } else { + var s *Student = nil + redBlackTree2.Set(i, s) + } + } + t.Assert(redBlackTree2.Size(), 5) + }) +} From 58f68e755d7311bbeb0dad553ad155e9abbfda14 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 14 Jan 2026 04:02:50 +0000 Subject: [PATCH 2/4] Apply gci import order changes --- container/gtree/gtree_z_k_v_tree_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/gtree/gtree_z_k_v_tree_test.go b/container/gtree/gtree_z_k_v_tree_test.go index f44b2d97538..e346ab66554 100644 --- a/container/gtree/gtree_z_k_v_tree_test.go +++ b/container/gtree/gtree_z_k_v_tree_test.go @@ -7,11 +7,11 @@ package gtree_test import ( - "github.com/gogf/gf/v2/util/gutil" "testing" "github.com/gogf/gf/v2/container/gtree" "github.com/gogf/gf/v2/test/gtest" + "github.com/gogf/gf/v2/util/gutil" ) func Test_KVAVLTree_TypedNil(t *testing.T) { From b4c5f964d305e5b59107ae2a1858783ee8629c30 Mon Sep 17 00:00:00 2001 From: shanyujie <1196661499@qq.com> Date: Wed, 14 Jan 2026 12:18:42 +0800 Subject: [PATCH 3/4] =?UTF-8?q?test(gmap):=20=E6=B7=BB=E5=8A=A0TypedNil?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- container/gmap/gmap_z_unit_k_v_map_test.go | 1 + container/gmap/gmap_z_unit_list_k_v_map_test.go | 1 + 2 files changed, 2 insertions(+) diff --git a/container/gmap/gmap_z_unit_k_v_map_test.go b/container/gmap/gmap_z_unit_k_v_map_test.go index 259f0d02607..a904c8b79f6 100644 --- a/container/gmap/gmap_z_unit_k_v_map_test.go +++ b/container/gmap/gmap_z_unit_k_v_map_test.go @@ -1631,6 +1631,7 @@ func Test_KVMap_Flip_String(t *testing.T) { }) } +// Test TypedNil with custom nil checker for pointers func Test_KVMap_TypedNil(t *testing.T) { gtest.C(t, func(t *gtest.T) { type Student struct { diff --git a/container/gmap/gmap_z_unit_list_k_v_map_test.go b/container/gmap/gmap_z_unit_list_k_v_map_test.go index 194e52c961f..4fa02d5e5ff 100644 --- a/container/gmap/gmap_z_unit_list_k_v_map_test.go +++ b/container/gmap/gmap_z_unit_list_k_v_map_test.go @@ -1342,6 +1342,7 @@ func Test_ListKVMap_UnmarshalValue_NilData(t *testing.T) { }) } +// Test typed nil values func Test_ListKVMap_TypedNil(t *testing.T) { gtest.C(t, func(t *gtest.T) { type Student struct { From 129c31f589fd725e782e3eee49a1c23ec93e09b6 Mon Sep 17 00:00:00 2001 From: Lance Add <1196661499@qq.com> Date: Thu, 15 Jan 2026 09:56:36 +0800 Subject: [PATCH 4/4] Update container/gset/gset_t_set.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- container/gset/gset_t_set.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/container/gset/gset_t_set.go b/container/gset/gset_t_set.go index 25dcdda59e2..4367ceee1e0 100644 --- a/container/gset/gset_t_set.go +++ b/container/gset/gset_t_set.go @@ -52,6 +52,8 @@ func NewTSetFrom[T comparable](items []T, safe ...bool) *TSet[T] { // The nil checker function takes an element of type T and returns a boolean indicating // whether the element should be treated as nil. func (set *TSet[T]) RegisterNilChecker(nilChecker NilChecker[T]) { + set.mu.Lock() + defer set.mu.Unlock() set.nilChecker = nilChecker }