github.com/gogf/gf/v2@v2.7.4/container/gmap/gmap_hash_str_int_map.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with gm file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  //
     7  
     8  package gmap
     9  
    10  import (
    11  	"github.com/gogf/gf/v2/internal/empty"
    12  	"github.com/gogf/gf/v2/internal/json"
    13  	"github.com/gogf/gf/v2/internal/rwmutex"
    14  	"github.com/gogf/gf/v2/util/gconv"
    15  )
    16  
    17  // StrIntMap implements map[string]int with RWMutex that has switch.
    18  type StrIntMap struct {
    19  	mu   rwmutex.RWMutex
    20  	data map[string]int
    21  }
    22  
    23  // NewStrIntMap returns an empty StrIntMap object.
    24  // The parameter `safe` is used to specify whether using map in concurrent-safety,
    25  // which is false in default.
    26  func NewStrIntMap(safe ...bool) *StrIntMap {
    27  	return &StrIntMap{
    28  		mu:   rwmutex.Create(safe...),
    29  		data: make(map[string]int),
    30  	}
    31  }
    32  
    33  // NewStrIntMapFrom creates and returns a hash map from given map `data`.
    34  // Note that, the param `data` map will be set as the underlying data map(no deep copy),
    35  // there might be some concurrent-safe issues when changing the map outside.
    36  func NewStrIntMapFrom(data map[string]int, safe ...bool) *StrIntMap {
    37  	return &StrIntMap{
    38  		mu:   rwmutex.Create(safe...),
    39  		data: data,
    40  	}
    41  }
    42  
    43  // Iterator iterates the hash map readonly with custom callback function `f`.
    44  // If `f` returns true, then it continues iterating; or false to stop.
    45  func (m *StrIntMap) Iterator(f func(k string, v int) bool) {
    46  	for k, v := range m.Map() {
    47  		if !f(k, v) {
    48  			break
    49  		}
    50  	}
    51  }
    52  
    53  // Clone returns a new hash map with copy of current map data.
    54  func (m *StrIntMap) Clone() *StrIntMap {
    55  	return NewStrIntMapFrom(m.MapCopy(), m.mu.IsSafe())
    56  }
    57  
    58  // Map returns the underlying data map.
    59  // Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
    60  // or else a pointer to the underlying data.
    61  func (m *StrIntMap) Map() map[string]int {
    62  	m.mu.RLock()
    63  	defer m.mu.RUnlock()
    64  	if !m.mu.IsSafe() {
    65  		return m.data
    66  	}
    67  	data := make(map[string]int, len(m.data))
    68  	for k, v := range m.data {
    69  		data[k] = v
    70  	}
    71  	return data
    72  }
    73  
    74  // MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
    75  func (m *StrIntMap) MapStrAny() map[string]interface{} {
    76  	m.mu.RLock()
    77  	defer m.mu.RUnlock()
    78  	data := make(map[string]interface{}, len(m.data))
    79  	for k, v := range m.data {
    80  		data[k] = v
    81  	}
    82  	return data
    83  }
    84  
    85  // MapCopy returns a copy of the underlying data of the hash map.
    86  func (m *StrIntMap) MapCopy() map[string]int {
    87  	m.mu.RLock()
    88  	defer m.mu.RUnlock()
    89  	data := make(map[string]int, len(m.data))
    90  	for k, v := range m.data {
    91  		data[k] = v
    92  	}
    93  	return data
    94  }
    95  
    96  // FilterEmpty deletes all key-value pair of which the value is empty.
    97  // Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
    98  func (m *StrIntMap) FilterEmpty() {
    99  	m.mu.Lock()
   100  	for k, v := range m.data {
   101  		if empty.IsEmpty(v) {
   102  			delete(m.data, k)
   103  		}
   104  	}
   105  	m.mu.Unlock()
   106  }
   107  
   108  // Set sets key-value to the hash map.
   109  func (m *StrIntMap) Set(key string, val int) {
   110  	m.mu.Lock()
   111  	if m.data == nil {
   112  		m.data = make(map[string]int)
   113  	}
   114  	m.data[key] = val
   115  	m.mu.Unlock()
   116  }
   117  
   118  // Sets batch sets key-values to the hash map.
   119  func (m *StrIntMap) Sets(data map[string]int) {
   120  	m.mu.Lock()
   121  	if m.data == nil {
   122  		m.data = data
   123  	} else {
   124  		for k, v := range data {
   125  			m.data[k] = v
   126  		}
   127  	}
   128  	m.mu.Unlock()
   129  }
   130  
   131  // Search searches the map with given `key`.
   132  // Second return parameter `found` is true if key was found, otherwise false.
   133  func (m *StrIntMap) Search(key string) (value int, found bool) {
   134  	m.mu.RLock()
   135  	if m.data != nil {
   136  		value, found = m.data[key]
   137  	}
   138  	m.mu.RUnlock()
   139  	return
   140  }
   141  
   142  // Get returns the value by given `key`.
   143  func (m *StrIntMap) Get(key string) (value int) {
   144  	m.mu.RLock()
   145  	if m.data != nil {
   146  		value = m.data[key]
   147  	}
   148  	m.mu.RUnlock()
   149  	return
   150  }
   151  
   152  // Pop retrieves and deletes an item from the map.
   153  func (m *StrIntMap) Pop() (key string, value int) {
   154  	m.mu.Lock()
   155  	defer m.mu.Unlock()
   156  	for key, value = range m.data {
   157  		delete(m.data, key)
   158  		return
   159  	}
   160  	return
   161  }
   162  
   163  // Pops retrieves and deletes `size` items from the map.
   164  // It returns all items if size == -1.
   165  func (m *StrIntMap) Pops(size int) map[string]int {
   166  	m.mu.Lock()
   167  	defer m.mu.Unlock()
   168  	if size > len(m.data) || size == -1 {
   169  		size = len(m.data)
   170  	}
   171  	if size == 0 {
   172  		return nil
   173  	}
   174  	var (
   175  		index  = 0
   176  		newMap = make(map[string]int, size)
   177  	)
   178  	for k, v := range m.data {
   179  		delete(m.data, k)
   180  		newMap[k] = v
   181  		index++
   182  		if index == size {
   183  			break
   184  		}
   185  	}
   186  	return newMap
   187  }
   188  
   189  // doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
   190  // if not exists, set value to the map with given `key`,
   191  // or else just return the existing value.
   192  //
   193  // It returns value with given `key`.
   194  func (m *StrIntMap) doSetWithLockCheck(key string, value int) int {
   195  	m.mu.Lock()
   196  	if m.data == nil {
   197  		m.data = make(map[string]int)
   198  	}
   199  	if v, ok := m.data[key]; ok {
   200  		m.mu.Unlock()
   201  		return v
   202  	}
   203  	m.data[key] = value
   204  	m.mu.Unlock()
   205  	return value
   206  }
   207  
   208  // GetOrSet returns the value by key,
   209  // or sets value with given `value` if it does not exist and then returns this value.
   210  func (m *StrIntMap) GetOrSet(key string, value int) int {
   211  	if v, ok := m.Search(key); !ok {
   212  		return m.doSetWithLockCheck(key, value)
   213  	} else {
   214  		return v
   215  	}
   216  }
   217  
   218  // GetOrSetFunc returns the value by key,
   219  // or sets value with returned value of callback function `f` if it does not exist
   220  // and then returns this value.
   221  func (m *StrIntMap) GetOrSetFunc(key string, f func() int) int {
   222  	if v, ok := m.Search(key); !ok {
   223  		return m.doSetWithLockCheck(key, f())
   224  	} else {
   225  		return v
   226  	}
   227  }
   228  
   229  // GetOrSetFuncLock returns the value by key,
   230  // or sets value with returned value of callback function `f` if it does not exist
   231  // and then returns this value.
   232  //
   233  // GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f`
   234  // with mutex.Lock of the hash map.
   235  func (m *StrIntMap) GetOrSetFuncLock(key string, f func() int) int {
   236  	if v, ok := m.Search(key); !ok {
   237  		m.mu.Lock()
   238  		defer m.mu.Unlock()
   239  		if m.data == nil {
   240  			m.data = make(map[string]int)
   241  		}
   242  		if v, ok = m.data[key]; ok {
   243  			return v
   244  		}
   245  		v = f()
   246  		m.data[key] = v
   247  		return v
   248  	} else {
   249  		return v
   250  	}
   251  }
   252  
   253  // SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true.
   254  // It returns false if `key` exists, and `value` would be ignored.
   255  func (m *StrIntMap) SetIfNotExist(key string, value int) bool {
   256  	if !m.Contains(key) {
   257  		m.doSetWithLockCheck(key, value)
   258  		return true
   259  	}
   260  	return false
   261  }
   262  
   263  // SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true.
   264  // It returns false if `key` exists, and `value` would be ignored.
   265  func (m *StrIntMap) SetIfNotExistFunc(key string, f func() int) bool {
   266  	if !m.Contains(key) {
   267  		m.doSetWithLockCheck(key, f())
   268  		return true
   269  	}
   270  	return false
   271  }
   272  
   273  // SetIfNotExistFuncLock sets value with return value of callback function `f`, and then returns true.
   274  // It returns false if `key` exists, and `value` would be ignored.
   275  //
   276  // SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
   277  // it executes function `f` with mutex.Lock of the hash map.
   278  func (m *StrIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
   279  	if !m.Contains(key) {
   280  		m.mu.Lock()
   281  		defer m.mu.Unlock()
   282  		if m.data == nil {
   283  			m.data = make(map[string]int)
   284  		}
   285  		if _, ok := m.data[key]; !ok {
   286  			m.data[key] = f()
   287  		}
   288  		return true
   289  	}
   290  	return false
   291  }
   292  
   293  // Removes batch deletes values of the map by keys.
   294  func (m *StrIntMap) Removes(keys []string) {
   295  	m.mu.Lock()
   296  	if m.data != nil {
   297  		for _, key := range keys {
   298  			delete(m.data, key)
   299  		}
   300  	}
   301  	m.mu.Unlock()
   302  }
   303  
   304  // Remove deletes value from map by given `key`, and return this deleted value.
   305  func (m *StrIntMap) Remove(key string) (value int) {
   306  	m.mu.Lock()
   307  	if m.data != nil {
   308  		var ok bool
   309  		if value, ok = m.data[key]; ok {
   310  			delete(m.data, key)
   311  		}
   312  	}
   313  	m.mu.Unlock()
   314  	return
   315  }
   316  
   317  // Keys returns all keys of the map as a slice.
   318  func (m *StrIntMap) Keys() []string {
   319  	m.mu.RLock()
   320  	var (
   321  		keys  = make([]string, len(m.data))
   322  		index = 0
   323  	)
   324  	for key := range m.data {
   325  		keys[index] = key
   326  		index++
   327  	}
   328  	m.mu.RUnlock()
   329  	return keys
   330  }
   331  
   332  // Values returns all values of the map as a slice.
   333  func (m *StrIntMap) Values() []int {
   334  	m.mu.RLock()
   335  	var (
   336  		values = make([]int, len(m.data))
   337  		index  = 0
   338  	)
   339  	for _, value := range m.data {
   340  		values[index] = value
   341  		index++
   342  	}
   343  	m.mu.RUnlock()
   344  	return values
   345  }
   346  
   347  // Contains checks whether a key exists.
   348  // It returns true if the `key` exists, or else false.
   349  func (m *StrIntMap) Contains(key string) bool {
   350  	var ok bool
   351  	m.mu.RLock()
   352  	if m.data != nil {
   353  		_, ok = m.data[key]
   354  	}
   355  	m.mu.RUnlock()
   356  	return ok
   357  }
   358  
   359  // Size returns the size of the map.
   360  func (m *StrIntMap) Size() int {
   361  	m.mu.RLock()
   362  	length := len(m.data)
   363  	m.mu.RUnlock()
   364  	return length
   365  }
   366  
   367  // IsEmpty checks whether the map is empty.
   368  // It returns true if map is empty, or else false.
   369  func (m *StrIntMap) IsEmpty() bool {
   370  	return m.Size() == 0
   371  }
   372  
   373  // Clear deletes all data of the map, it will remake a new underlying data map.
   374  func (m *StrIntMap) Clear() {
   375  	m.mu.Lock()
   376  	m.data = make(map[string]int)
   377  	m.mu.Unlock()
   378  }
   379  
   380  // Replace the data of the map with given `data`.
   381  func (m *StrIntMap) Replace(data map[string]int) {
   382  	m.mu.Lock()
   383  	m.data = data
   384  	m.mu.Unlock()
   385  }
   386  
   387  // LockFunc locks writing with given callback function `f` within RWMutex.Lock.
   388  func (m *StrIntMap) LockFunc(f func(m map[string]int)) {
   389  	m.mu.Lock()
   390  	defer m.mu.Unlock()
   391  	f(m.data)
   392  }
   393  
   394  // RLockFunc locks reading with given callback function `f` within RWMutex.RLock.
   395  func (m *StrIntMap) RLockFunc(f func(m map[string]int)) {
   396  	m.mu.RLock()
   397  	defer m.mu.RUnlock()
   398  	f(m.data)
   399  }
   400  
   401  // Flip exchanges key-value of the map to value-key.
   402  func (m *StrIntMap) Flip() {
   403  	m.mu.Lock()
   404  	defer m.mu.Unlock()
   405  	n := make(map[string]int, len(m.data))
   406  	for k, v := range m.data {
   407  		n[gconv.String(v)] = gconv.Int(k)
   408  	}
   409  	m.data = n
   410  }
   411  
   412  // Merge merges two hash maps.
   413  // The `other` map will be merged into the map `m`.
   414  func (m *StrIntMap) Merge(other *StrIntMap) {
   415  	m.mu.Lock()
   416  	defer m.mu.Unlock()
   417  	if m.data == nil {
   418  		m.data = other.MapCopy()
   419  		return
   420  	}
   421  	if other != m {
   422  		other.mu.RLock()
   423  		defer other.mu.RUnlock()
   424  	}
   425  	for k, v := range other.data {
   426  		m.data[k] = v
   427  	}
   428  }
   429  
   430  // String returns the map as a string.
   431  func (m *StrIntMap) String() string {
   432  	if m == nil {
   433  		return ""
   434  	}
   435  	b, _ := m.MarshalJSON()
   436  	return string(b)
   437  }
   438  
   439  // MarshalJSON implements the interface MarshalJSON for json.Marshal.
   440  func (m StrIntMap) MarshalJSON() ([]byte, error) {
   441  	m.mu.RLock()
   442  	defer m.mu.RUnlock()
   443  	return json.Marshal(m.data)
   444  }
   445  
   446  // UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
   447  func (m *StrIntMap) UnmarshalJSON(b []byte) error {
   448  	m.mu.Lock()
   449  	defer m.mu.Unlock()
   450  	if m.data == nil {
   451  		m.data = make(map[string]int)
   452  	}
   453  	if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
   454  		return err
   455  	}
   456  	return nil
   457  }
   458  
   459  // UnmarshalValue is an interface implement which sets any type of value for map.
   460  func (m *StrIntMap) UnmarshalValue(value interface{}) (err error) {
   461  	m.mu.Lock()
   462  	defer m.mu.Unlock()
   463  	if m.data == nil {
   464  		m.data = make(map[string]int)
   465  	}
   466  	switch value.(type) {
   467  	case string, []byte:
   468  		return json.UnmarshalUseNumber(gconv.Bytes(value), &m.data)
   469  	default:
   470  		for k, v := range gconv.Map(value) {
   471  			m.data[k] = gconv.Int(v)
   472  		}
   473  	}
   474  	return
   475  }
   476  
   477  // DeepCopy implements interface for deep copy of current type.
   478  func (m *StrIntMap) DeepCopy() interface{} {
   479  	if m == nil {
   480  		return nil
   481  	}
   482  	m.mu.RLock()
   483  	defer m.mu.RUnlock()
   484  	data := make(map[string]int, len(m.data))
   485  	for k, v := range m.data {
   486  		data[k] = v
   487  	}
   488  	return NewStrIntMapFrom(data, m.mu.IsSafe())
   489  }
   490  
   491  // IsSubOf checks whether the current map is a sub-map of `other`.
   492  func (m *StrIntMap) IsSubOf(other *StrIntMap) bool {
   493  	if m == other {
   494  		return true
   495  	}
   496  	m.mu.RLock()
   497  	defer m.mu.RUnlock()
   498  	other.mu.RLock()
   499  	defer other.mu.RUnlock()
   500  	for key, value := range m.data {
   501  		otherValue, ok := other.data[key]
   502  		if !ok {
   503  			return false
   504  		}
   505  		if otherValue != value {
   506  			return false
   507  		}
   508  	}
   509  	return true
   510  }
   511  
   512  // Diff compares current map `m` with map `other` and returns their different keys.
   513  // The returned `addedKeys` are the keys that are in map `m` but not in map `other`.
   514  // The returned `removedKeys` are the keys that are in map `other` but not in map `m`.
   515  // The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`).
   516  func (m *StrIntMap) Diff(other *StrIntMap) (addedKeys, removedKeys, updatedKeys []string) {
   517  	m.mu.RLock()
   518  	defer m.mu.RUnlock()
   519  	other.mu.RLock()
   520  	defer other.mu.RUnlock()
   521  
   522  	for key := range m.data {
   523  		if _, ok := other.data[key]; !ok {
   524  			removedKeys = append(removedKeys, key)
   525  		} else if m.data[key] != other.data[key] {
   526  			updatedKeys = append(updatedKeys, key)
   527  		}
   528  	}
   529  	for key := range other.data {
   530  		if _, ok := m.data[key]; !ok {
   531  			addedKeys = append(addedKeys, key)
   532  		}
   533  	}
   534  	return
   535  }