Files
gf/container/gmap/gmap_hash_any_any_map.go
hailaz ee24da4e72 refactor: interface{} to any and reflect.Ptr to reflect.Pointer (#4395)
This pull request standardizes the use of the Go 1.18+ `any` type alias
instead of `interface{}` throughout the codebase. The change improves
code readability and aligns with modern Go best practices. The update
touches many files, including core data structures, code generation
templates, logging utilities, and test data, ensuring consistency across
all usages.

**Type alias migration to `any`:**

* Replaced all instances of `interface{}` with `any` in core data
structures such as `garray` and in generated model structs (e.g.,
`TableUser`, `User1`, `User2`) to modernize type usage.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[3]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[4]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[5]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[6]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
* Updated function signatures, method parameters, and return types from
`interface{}` to `any` in various parts of the codebase, including code
generation, service logic, and logging utilities (e.g., `mlog`).
[[1]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[2]](diffhunk://#diff-2b1953fb78cf3593d8c2c7d911e95b65fd0b847c30ed0b4d167d16fe6d781235L54-R74)
[[3]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[4]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
[[5]](diffhunk://#diff-c5d51d56f487779a2b6207c7ad26c7a20bbadcc846ce094fe60ab4cabff58c51L107-R107)
[[6]](diffhunk://#diff-f96e6a9fdb416eb1804ceaba1fe0ac637bff22c43837f8bb849c2366ce72d4a1L116-R121)
[[7]](diffhunk://#diff-f94c83a1b08ae060d9346f4a6031fc4a7b9a0b894e02d9afaa09018b6598eac0L112-R112)
[[8]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L36-R36)
[[9]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L74-R74)
[[10]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L96-R96)

**Generated code and templates:**

* Adjusted generated files and code generation templates to output `any`
instead of `interface{}` for relevant struct fields and function
signatures, ensuring that new code generation aligns with the updated
convention.
[[1]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[2]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[3]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[4]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[5]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
[[6]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[7]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[8]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)

**Container and utility updates:**

* Refactored the `garray` container implementation and related
constructors/methods to use `[]any` instead of `[]interface{}`, along
with corresponding function signatures.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L52-R52)
[[3]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L62-R62)
[[4]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L73-R86)
[[5]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L96-R97)
[[6]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L107-R114)
[[7]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L124-R124)
[[8]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L135-R143)
[[9]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L167-R167)

These changes collectively modernize the codebase and prepare it for
future Go developments by using the idiomatic `any` type.
2025-08-28 16:53:19 +08:00

563 lines
14 KiB
Go

// 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap
import (
"reflect"
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/internal/deepcopy"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/rwmutex"
"github.com/gogf/gf/v2/util/gconv"
)
// AnyAnyMap wraps map type `map[any]any` and provides more map features.
type AnyAnyMap struct {
mu rwmutex.RWMutex
data map[any]any
}
// NewAnyAnyMap creates and returns an empty hash map.
// The parameter `safe` is used to specify whether using map in concurrent-safety,
// which is false in default.
func NewAnyAnyMap(safe ...bool) *AnyAnyMap {
return &AnyAnyMap{
mu: rwmutex.Create(safe...),
data: make(map[any]any),
}
}
// NewAnyAnyMapFrom creates and 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[any]any, safe ...bool) *AnyAnyMap {
return &AnyAnyMap{
mu: rwmutex.Create(safe...),
data: data,
}
}
// 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 *AnyAnyMap) Iterator(f func(k any, v any) bool) {
for k, v := range m.Map() {
if !f(k, v) {
break
}
}
}
// Clone returns a new hash map with copy of current map data.
func (m *AnyAnyMap) Clone(safe ...bool) *AnyAnyMap {
return NewFrom(m.MapCopy(), safe...)
}
// Map returns the underlying data map.
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
// or else a pointer to the underlying data.
func (m *AnyAnyMap) Map() map[any]any {
m.mu.RLock()
defer m.mu.RUnlock()
if !m.mu.IsSafe() {
return m.data
}
data := make(map[any]any, len(m.data))
for k, v := range m.data {
data[k] = v
}
return data
}
// MapCopy returns a shallow copy of the underlying data of the hash map.
func (m *AnyAnyMap) MapCopy() map[any]any {
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[any]any, len(m.data))
for k, v := range m.data {
data[k] = v
}
return data
}
// MapStrAny returns a copy of the underlying data of the map as map[string]any.
func (m *AnyAnyMap) MapStrAny() map[string]any {
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[string]any, len(m.data))
for k, v := range m.data {
data[gconv.String(k)] = v
}
return data
}
// FilterEmpty deletes all key-value pair of which the value is empty.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (m *AnyAnyMap) FilterEmpty() {
m.mu.Lock()
defer m.mu.Unlock()
for k, v := range m.data {
if empty.IsEmpty(v) {
delete(m.data, k)
}
}
}
// FilterNil deletes all key-value pair of which the value is nil.
func (m *AnyAnyMap) FilterNil() {
m.mu.Lock()
defer m.mu.Unlock()
for k, v := range m.data {
if empty.IsNil(v) {
delete(m.data, k)
}
}
}
// Set sets key-value to the hash map.
func (m *AnyAnyMap) Set(key any, value any) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[any]any)
}
m.data[key] = value
m.mu.Unlock()
}
// Sets batch sets key-values to the hash map.
func (m *AnyAnyMap) Sets(data map[any]any) {
m.mu.Lock()
if m.data == nil {
m.data = data
} else {
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 any) (value any, found bool) {
m.mu.RLock()
if m.data != nil {
value, found = m.data[key]
}
m.mu.RUnlock()
return
}
// Get returns the value by given `key`.
func (m *AnyAnyMap) Get(key any) (value any) {
m.mu.RLock()
if m.data != nil {
value = m.data[key]
}
m.mu.RUnlock()
return
}
// Pop retrieves and deletes an item from the map.
func (m *AnyAnyMap) Pop() (key, value any) {
m.mu.Lock()
defer m.mu.Unlock()
for key, value = range m.data {
delete(m.data, key)
return
}
return
}
// Pops retrieves and deletes `size` items from the map.
// It returns all items if size == -1.
func (m *AnyAnyMap) Pops(size int) map[any]any {
m.mu.Lock()
defer m.mu.Unlock()
if size > len(m.data) || size == -1 {
size = len(m.data)
}
if size == 0 {
return nil
}
var (
index = 0
newMap = make(map[any]any, size)
)
for k, v := range m.data {
delete(m.data, k)
newMap[k] = v
index++
if index == size {
break
}
}
return newMap
}
// 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 any, value any) any {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[any]any)
}
if v, ok := m.data[key]; ok {
return v
}
if f, ok := value.(func() any); ok {
value = f()
}
if value != nil {
m.data[key] = value
}
return value
}
// GetOrSet returns the value by key,
// or sets value with given `value` if it does not exist and then returns this value.
func (m *AnyAnyMap) GetOrSet(key any, value any) any {
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 returned value of callback function `f` if it does not exist
// and then returns this value.
func (m *AnyAnyMap) GetOrSetFunc(key any, f func() any) any {
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 returned value of callback function `f` if it does not exist
// and then 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 any, f func() any) any {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f)
} else {
return v
}
}
// GetVar returns a Var with the value by given `key`.
// The returned Var is un-concurrent safe.
func (m *AnyAnyMap) GetVar(key any) *gvar.Var {
return gvar.New(m.Get(key))
}
// GetVarOrSet returns a Var with result from GetOrSet.
// The returned Var is un-concurrent safe.
func (m *AnyAnyMap) GetVarOrSet(key any, value any) *gvar.Var {
return gvar.New(m.GetOrSet(key, value))
}
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
// The returned Var is un-concurrent safe.
func (m *AnyAnyMap) GetVarOrSetFunc(key any, f func() any) *gvar.Var {
return gvar.New(m.GetOrSetFunc(key, f))
}
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
// The returned Var is un-concurrent safe.
func (m *AnyAnyMap) GetVarOrSetFuncLock(key any, f func() any) *gvar.Var {
return gvar.New(m.GetOrSetFuncLock(key, f))
}
// SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true.
// It returns false if `key` exists, and `value` would be ignored.
func (m *AnyAnyMap) SetIfNotExist(key any, value any) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true.
// It returns false if `key` exists, and `value` would be ignored.
func (m *AnyAnyMap) SetIfNotExistFunc(key any, f func() any) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, f())
return true
}
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function `f`, and then returns 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 any, f func() any) 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 any) (value any) {
m.mu.Lock()
if m.data != nil {
var ok bool
if value, ok = m.data[key]; ok {
delete(m.data, key)
}
}
m.mu.Unlock()
return
}
// Removes batch deletes values of the map by keys.
func (m *AnyAnyMap) Removes(keys []any) {
m.mu.Lock()
if m.data != nil {
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() []any {
m.mu.RLock()
defer m.mu.RUnlock()
var (
keys = make([]any, len(m.data))
index = 0
)
for key := range m.data {
keys[index] = key
index++
}
return keys
}
// Values returns all values of the map as a slice.
func (m *AnyAnyMap) Values() []any {
m.mu.RLock()
defer m.mu.RUnlock()
var (
values = make([]any, len(m.data))
index = 0
)
for _, value := range m.data {
values[index] = value
index++
}
return values
}
// Contains checks whether a key exists.
// It returns true if the `key` exists, or else false.
func (m *AnyAnyMap) Contains(key any) bool {
var ok bool
m.mu.RLock()
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return ok
}
// 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 {
return m.Size() == 0
}
// 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[any]any)
m.mu.Unlock()
}
// Replace the data of the map with given `data`.
func (m *AnyAnyMap) Replace(data map[any]any) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function `f` within RWMutex.Lock.
func (m *AnyAnyMap) LockFunc(f func(m map[any]any)) {
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[any]any)) {
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[any]any, 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 m.data == nil {
m.data = other.MapCopy()
return
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
for k, v := range other.data {
m.data[k] = v
}
}
// String returns the map as a string.
func (m *AnyAnyMap) String() string {
if m == nil {
return ""
}
b, _ := m.MarshalJSON()
return string(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m AnyAnyMap) MarshalJSON() ([]byte, error) {
return json.Marshal(gconv.Map(m.Map()))
}
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *AnyAnyMap) UnmarshalJSON(b []byte) error {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[any]any)
}
var data map[string]any
if err := json.UnmarshalUseNumber(b, &data); err != nil {
return err
}
for k, v := range data {
m.data[k] = v
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *AnyAnyMap) UnmarshalValue(value any) (err error) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[any]any)
}
for k, v := range gconv.Map(value) {
m.data[k] = v
}
return
}
// DeepCopy implements interface for deep copy of current type.
func (m *AnyAnyMap) DeepCopy() any {
if m == nil {
return nil
}
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[any]any, len(m.data))
for k, v := range m.data {
data[k] = deepcopy.Copy(v)
}
return NewFrom(data, m.mu.IsSafe())
}
// IsSubOf checks whether the current map is a sub-map of `other`.
func (m *AnyAnyMap) IsSubOf(other *AnyAnyMap) bool {
if m == other {
return true
}
m.mu.RLock()
defer m.mu.RUnlock()
other.mu.RLock()
defer other.mu.RUnlock()
for key, value := range m.data {
otherValue, ok := other.data[key]
if !ok {
return false
}
if otherValue != value {
return false
}
}
return true
}
// Diff compares current map `m` with map `other` and returns their different keys.
// The returned `addedKeys` are the keys that are in map `m` but not in map `other`.
// The returned `removedKeys` are the keys that are in map `other` but not in map `m`.
// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`).
func (m *AnyAnyMap) Diff(other *AnyAnyMap) (addedKeys, removedKeys, updatedKeys []any) {
m.mu.RLock()
defer m.mu.RUnlock()
other.mu.RLock()
defer other.mu.RUnlock()
for key := range m.data {
if _, ok := other.data[key]; !ok {
removedKeys = append(removedKeys, key)
} else if !reflect.DeepEqual(m.data[key], other.data[key]) {
updatedKeys = append(updatedKeys, key)
}
}
for key := range other.data {
if _, ok := m.data[key]; !ok {
addedKeys = append(addedKeys, key)
}
}
return
}