diff --git a/g/container/gmap/gmap.go b/g/container/gmap/gmap.go index d5d3e1291..e749acaad 100644 --- a/g/container/gmap/gmap.go +++ b/g/container/gmap/gmap.go @@ -103,6 +103,15 @@ func (m *Map) Sets(data map[interface{}]interface{}) { m.mu.Unlock() } +// Search searches the map with given . +// Second return parameter is true if key was found, otherwise false. +func (m *Map) 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 . func (m *Map) Get(key interface{}) interface{} { m.mu.RLock() @@ -136,7 +145,7 @@ func (m *Map) doSetWithLockCheck(key interface{}, value interface{}) interface{} // GetOrSet returns the value by key, // or set value with given if not exist and returns this value. func (m *Map) GetOrSet(key interface{}, value interface{}) interface{} { - if v := m.Get(key); v == nil { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, value) } else { return v @@ -147,7 +156,7 @@ func (m *Map) GetOrSet(key interface{}, value interface{}) interface{} { // or sets value with return value of callback function if not exist // and returns this value. func (m *Map) GetOrSetFunc(key interface{}, f func() interface{}) interface{} { - if v := m.Get(key); v == nil { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, f()) } else { return v @@ -161,7 +170,7 @@ func (m *Map) GetOrSetFunc(key interface{}, f func() interface{}) interface{} { // GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function // with mutex.Lock of the hash map. func (m *Map) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} { - if v := m.Get(key); v == nil { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, f) } else { return v @@ -314,13 +323,13 @@ func (m *Map) RLockFunc(f func(m map[interface{}]interface{})) { f(m.data) } -// Flip exchanges key-value of the map, it will change key-value to value-key. +// Flip exchanges key-value of the map to value-key. func (m *Map) Flip() { m.mu.Lock() defer m.mu.Unlock() n := make(map[interface{}]interface{}, len(m.data)) - for i, v := range m.data { - n[v] = i + for k, v := range m.data { + n[v] = k } m.data = n } diff --git a/g/container/gmap/gmap_int_any_map.go b/g/container/gmap/gmap_int_any_map.go index 218091385..e3da3b9bd 100644 --- a/g/container/gmap/gmap_int_any_map.go +++ b/g/container/gmap/gmap_int_any_map.go @@ -104,6 +104,15 @@ func (m *IntAnyMap) Sets(data map[int]interface{}) { m.mu.Unlock() } +// Search searches the map with given . +// Second return parameter 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 . func (m *IntAnyMap) Get(key int) (interface{}) { m.mu.RLock() @@ -140,7 +149,7 @@ func (m *IntAnyMap) doSetWithLockCheck(key int, value interface{}) interface{} { // GetOrSet returns the value by key, // or set value with given if not exist and returns this value. func (m *IntAnyMap) GetOrSet(key int, value interface{}) interface{} { - if v := m.Get(key); v == nil { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, value) } else { return v @@ -150,7 +159,7 @@ func (m *IntAnyMap) GetOrSet(key int, value interface{}) interface{} { // GetOrSetFunc returns the value by key, // or sets value with return value of callback function if not exist and returns this value. func (m *IntAnyMap) GetOrSetFunc(key int, f func() interface{}) interface{} { - if v := m.Get(key); v == nil { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, f()) } else { return v @@ -163,7 +172,7 @@ func (m *IntAnyMap) GetOrSetFunc(key int, f func() interface{}) interface{} { // GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function // with mutex.Lock of the hash map. func (m *IntAnyMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} { - if v := m.Get(key); v == nil { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, f) } else { return v @@ -317,7 +326,7 @@ func (m *IntAnyMap) RLockFunc(f func(m map[int]interface{})) { f(m.data) } -// Flip exchanges key-value of the map, it will change key-value to value-key. +// Flip exchanges key-value of the map to value-key. func (m *IntAnyMap) Flip() { m.mu.Lock() defer m.mu.Unlock() diff --git a/g/container/gmap/gmap_int_int_map.go b/g/container/gmap/gmap_int_int_map.go index 30c448b35..798931742 100644 --- a/g/container/gmap/gmap_int_int_map.go +++ b/g/container/gmap/gmap_int_int_map.go @@ -101,6 +101,15 @@ func (m *IntIntMap) Sets(data map[int]int) { m.mu.Unlock() } +// Search searches the map with given . +// Second return parameter 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 . func (m *IntIntMap) Get(key int) (int) { m.mu.RLock() @@ -128,10 +137,7 @@ func (m *IntIntMap) doSetWithLockCheck(key int, value int) int { // GetOrSet returns the value by key, // or set value with given if not exist and returns this value. func (m *IntIntMap) GetOrSet(key int, value int) int { - m.mu.RLock() - v, ok := m.data[key] - m.mu.RUnlock() - if !ok { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, value) } else { return v @@ -141,10 +147,7 @@ func (m *IntIntMap) GetOrSet(key int, value int) int { // GetOrSetFunc returns the value by key, // or sets value with return value of callback function if not exist and returns this value. func (m *IntIntMap) GetOrSetFunc(key int, f func() int) int { - m.mu.RLock() - v, ok := m.data[key] - m.mu.RUnlock() - if !ok { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, f()) } else { return v @@ -157,20 +160,17 @@ func (m *IntIntMap) GetOrSetFunc(key int, f func() int) int { // GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function // with mutex.Lock of the hash map. func (m *IntIntMap) GetOrSetFuncLock(key int, f func() int) int { - m.mu.RLock() - val, ok := m.data[key] - m.mu.RUnlock() - if !ok { + if v, ok := m.Search(key); !ok { m.mu.Lock() defer m.mu.Unlock() - if v, ok := m.data[key]; ok { + if v, ok = m.data[key]; ok { return v } - val = f() - m.data[key] = val - return val + v = f() + m.data[key] = v + return v } else { - return val + return v } } @@ -300,7 +300,7 @@ func (m *IntIntMap) RLockFunc(f func(m map[int]int)) { f(m.data) } -// Flip exchanges key-value of the map, it will change key-value to value-key. +// Flip exchanges key-value of the map to value-key. func (m *IntIntMap) Flip() { m.mu.Lock() defer m.mu.Unlock() diff --git a/g/container/gmap/gmap_int_str_map.go b/g/container/gmap/gmap_int_str_map.go index 9c6993375..d9e2d0c49 100644 --- a/g/container/gmap/gmap_int_str_map.go +++ b/g/container/gmap/gmap_int_str_map.go @@ -102,6 +102,15 @@ func (m *IntStrMap) Sets(data map[int]string) { m.mu.Unlock() } +// Search searches the map with given . +// Second return parameter 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 . func (m *IntStrMap) Get(key int) string { m.mu.RLock() @@ -129,10 +138,7 @@ func (m *IntStrMap) doSetWithLockCheck(key int, value string) string { // GetOrSet returns the value by key, // or set value with given if not exist and returns this value. func (m *IntStrMap) GetOrSet(key int, value string) string { - m.mu.RLock() - v, ok := m.data[key] - m.mu.RUnlock() - if !ok { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, value) } else { return v @@ -142,10 +148,7 @@ func (m *IntStrMap) GetOrSet(key int, value string) string { // GetOrSetFunc returns the value by key, // or sets value with return value of callback function if not exist and returns this value. func (m *IntStrMap) GetOrSetFunc(key int, f func() string) string { - m.mu.RLock() - v, ok := m.data[key] - m.mu.RUnlock() - if !ok { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, f()) } else { return v @@ -158,20 +161,17 @@ func (m *IntStrMap) GetOrSetFunc(key int, f func() string) string { // GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function // with mutex.Lock of the hash map. func (m *IntStrMap) GetOrSetFuncLock(key int, f func() string) string { - m.mu.RLock() - val, ok := m.data[key] - m.mu.RUnlock() - if !ok { + if v, ok := m.Search(key); !ok { m.mu.Lock() defer m.mu.Unlock() - if v, ok := m.data[key]; ok { + if v, ok = m.data[key]; ok { return v } - val = f() - m.data[key] = val - return val + v = f() + m.data[key] = v + return v } else { - return val + return v } } @@ -301,7 +301,7 @@ func (m *IntStrMap) RLockFunc(f func(m map[int]string)) { f(m.data) } -// Flip exchanges key-value of the map, it will change key-value to value-key. +// Flip exchanges key-value of the map to value-key. func (m *IntStrMap) Flip() { m.mu.Lock() defer m.mu.Unlock() diff --git a/g/container/gmap/gmap_str_any_map.go b/g/container/gmap/gmap_str_any_map.go index 7a9e02ef6..d076c2bdd 100644 --- a/g/container/gmap/gmap_str_any_map.go +++ b/g/container/gmap/gmap_str_any_map.go @@ -104,6 +104,15 @@ func (m *StrAnyMap) Sets(data map[string]interface{}) { m.mu.Unlock() } +// Search searches the map with given . +// Second return parameter 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 . func (m *StrAnyMap) Get(key string) interface{} { m.mu.RLock() @@ -139,7 +148,7 @@ func (m *StrAnyMap) doSetWithLockCheck(key string, value interface{}) interface{ // GetOrSet returns the value by key, // or set value with given if not exist and returns this value. func (m *StrAnyMap) GetOrSet(key string, value interface{}) interface{} { - if v := m.Get(key); v == nil { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, value) } else { return v @@ -150,7 +159,7 @@ func (m *StrAnyMap) GetOrSet(key string, value interface{}) interface{} { // or sets value with return value of callback function if not exist // and returns this value. func (m *StrAnyMap) GetOrSetFunc(key string, f func() interface{}) interface{} { - if v := m.Get(key); v == nil { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, f()) } else { return v @@ -164,7 +173,7 @@ func (m *StrAnyMap) GetOrSetFunc(key string, f func() interface{}) interface{} { // GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function // with mutex.Lock of the hash map. func (m *StrAnyMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} { - if v := m.Get(key); v == nil { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, f) } else { return v @@ -317,7 +326,7 @@ func (m *StrAnyMap) RLockFunc(f func(m map[string]interface{})) { f(m.data) } -// Flip exchanges key-value of the map, it will change key-value to value-key. +// Flip exchanges key-value of the map to value-key. func (m *StrAnyMap) Flip() { m.mu.Lock() defer m.mu.Unlock() diff --git a/g/container/gmap/gmap_str_int_map.go b/g/container/gmap/gmap_str_int_map.go index e29505b8b..998247242 100644 --- a/g/container/gmap/gmap_str_int_map.go +++ b/g/container/gmap/gmap_str_int_map.go @@ -103,6 +103,15 @@ func (m *StrIntMap) Sets(data map[string]int) { m.mu.Unlock() } +// Search searches the map with given . +// Second return parameter 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 . func (m *StrIntMap) Get(key string) int { m.mu.RLock() @@ -130,10 +139,7 @@ func (m *StrIntMap) doSetWithLockCheck(key string, value int) int { // GetOrSet returns the value by key, // or set value with given if not exist and returns this value. func (m *StrIntMap) GetOrSet(key string, value int) int { - m.mu.RLock() - v, ok := m.data[key] - m.mu.RUnlock() - if !ok { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, value) } else { return v @@ -144,10 +150,7 @@ func (m *StrIntMap) GetOrSet(key string, value int) int { // or sets value with return value of callback function if not exist // and returns this value. func (m *StrIntMap) GetOrSetFunc(key string, f func() int) int { - m.mu.RLock() - v, ok := m.data[key] - m.mu.RUnlock() - if !ok { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, f()) } else { return v @@ -161,20 +164,17 @@ func (m *StrIntMap) GetOrSetFunc(key string, f func() int) int { // GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function // with mutex.Lock of the hash map. func (m *StrIntMap) GetOrSetFuncLock(key string, f func() int) int { - m.mu.RLock() - val, ok := m.data[key] - m.mu.RUnlock() - if !ok { + if v, ok := m.Search(key); !ok { m.mu.Lock() defer m.mu.Unlock() - if v, ok := m.data[key]; ok { + if v, ok = m.data[key]; ok { return v } - val = f() - m.data[key] = val - return val + v = f() + m.data[key] = v + return v } else { - return val + return v } } @@ -304,7 +304,7 @@ func (m *StrIntMap) RLockFunc(f func(m map[string]int)) { f(m.data) } -// Flip exchanges key-value of the map, it will change key-value to value-key. +// Flip exchanges key-value of the map to value-key. func (m *StrIntMap) Flip() { m.mu.Lock() defer m.mu.Unlock() diff --git a/g/container/gmap/gmap_str_str_map.go b/g/container/gmap/gmap_str_str_map.go index 929da8e2b..ac445887a 100644 --- a/g/container/gmap/gmap_str_str_map.go +++ b/g/container/gmap/gmap_str_str_map.go @@ -102,6 +102,15 @@ func (m *StrStrMap) Sets(data map[string]string) { m.mu.Unlock() } +// Search searches the map with given . +// Second return parameter 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 . func (m *StrStrMap) Get(key string) string { m.mu.RLock() @@ -129,10 +138,7 @@ func (m *StrStrMap) doSetWithLockCheck(key string, value string) string { // GetOrSet returns the value by key, // or set value with given if not exist and returns this value. func (m *StrStrMap) GetOrSet(key string, value string) string { - m.mu.RLock() - v, ok := m.data[key] - m.mu.RUnlock() - if !ok { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, value) } else { return v @@ -143,10 +149,7 @@ func (m *StrStrMap) GetOrSet(key string, value string) string { // or sets value with return value of callback function if not exist // and returns this value. func (m *StrStrMap) GetOrSetFunc(key string, f func() string) string { - m.mu.RLock() - v, ok := m.data[key] - m.mu.RUnlock() - if !ok { + if v, ok := m.Search(key); !ok { return m.doSetWithLockCheck(key, f()) } else { return v @@ -160,20 +163,17 @@ func (m *StrStrMap) GetOrSetFunc(key string, f func() string) string { // GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function // with mutex.Lock of the hash map. func (m *StrStrMap) GetOrSetFuncLock(key string, f func() string) string { - m.mu.RLock() - val, ok := m.data[key] - m.mu.RUnlock() - if !ok { + if v, ok := m.Search(key); !ok { m.mu.Lock() defer m.mu.Unlock() - if v, ok := m.data[key]; ok { + if v, ok = m.data[key]; ok { return v } - val = f() - m.data[key] = val - return val + v = f() + m.data[key] = v + return v } else { - return val + return v } } @@ -303,7 +303,7 @@ func (m *StrStrMap) RLockFunc(f func(m map[string]string)) { f(m.data) } -// Flip exchanges key-value of the map, it will change key-value to value-key. +// Flip exchanges key-value of the map to value-key. func (m *StrStrMap) Flip() { m.mu.Lock() defer m.mu.Unlock() diff --git a/g/container/gtree/gtree.go b/g/container/gtree/gtree.go new file mode 100644 index 000000000..fdb1e9132 --- /dev/null +++ b/g/container/gtree/gtree.go @@ -0,0 +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 this file, +// You can obtain one at https://github.com/gogf/gf. + +// Package gtree provides concurrent-safe/unsafe tree containers. +package gtree diff --git a/g/container/gtree/gtree_redblacktree.go b/g/container/gtree/gtree_redblacktree.go index 0eff5e850..38737915a 100644 --- a/g/container/gtree/gtree_redblacktree.go +++ b/g/container/gtree/gtree_redblacktree.go @@ -8,6 +8,7 @@ package gtree import ( "fmt" + "github.com/gogf/gf/g/container/gvar" "github.com/gogf/gf/g/internal/rwmutex" ) @@ -27,8 +28,8 @@ type RedBlackTree struct { // RedBlackTreeNode is a single element within the tree type RedBlackTreeNode struct { - Key interface{} - Value interface{} + key interface{} + value interface{} color color left *RedBlackTreeNode right *RedBlackTreeNode @@ -69,27 +70,27 @@ func (tree *RedBlackTree) Sets(data map[interface{}]interface{}) { } } -// doSet inserts key-value item into the tree. +// 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} + 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) + compare := tree.comparator(key, node.key) switch { case compare == 0: - node.Key = key - node.Value = value + //node.key = key + node.value = value return case compare < 0: if node.left == nil { - node.left = &RedBlackTreeNode{Key: key, Value: value, color: red} + node.left = &RedBlackTreeNode{key: key, value: value, color: red} insertedNode = node.left loop = false } else { @@ -97,7 +98,7 @@ func (tree *RedBlackTree) doSet(key interface{}, value interface{}) { } case compare > 0: if node.right == nil { - node.right = &RedBlackTreeNode{Key: key, Value: value, color: red} + node.right = &RedBlackTreeNode{key: key, value: value, color: red} insertedNode = node.right loop = false } else { @@ -113,22 +114,132 @@ func (tree *RedBlackTree) doSet(key interface{}, value interface{}) { // Get searches the node in the tree by and returns its value or nil if key is not found in tree. func (tree *RedBlackTree) Get(key interface{}) (value interface{}) { - node := tree.Search(key) - if node != nil { - return node.Value + 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 , +// or else just return the existing value. +// +// When setting value, if is type of , +// it will be executed with mutex.Lock of the hash map, +// and its return value will be set to the map with . +// +// It returns value with given . +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 } - return nil + 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 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 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 if not exist +// and returns this value. +// +// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function +// 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 . +// 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 to the map if the does not exist, then return true. +// It returns false if exists, and 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 , then return true. +// It returns false if exists, and 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 , then return true. +// It returns false if exists, and would be ignored. +// +// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that +// it executes function 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 exists in the tree. func (tree *RedBlackTree) Contains(key interface{}) bool { - return tree.Search(key) != nil + _, ok := tree.Search(key) + return ok } -// Remove removes the node from the tree by . -func (tree *RedBlackTree) Remove(key interface{}) (value interface{}) { - tree.mu.Lock() - defer tree.mu.Unlock() +// doRemove removes the node from the tree by without mutex. +func (tree *RedBlackTree) doRemove(key interface{}) (value interface{}) { child := (*RedBlackTreeNode)(nil) node := tree.doSearch(key) if node == nil { @@ -136,8 +247,8 @@ func (tree *RedBlackTree) Remove(key interface{}) (value interface{}) { } if node.left != nil && node.right != nil { p := node.left.maximumNode() - node.Key = p.Key - node.Value = p.Value + node.key = p.key + node.value = p.value node = p } if node.left == nil || node.right == nil { @@ -156,10 +267,26 @@ func (tree *RedBlackTree) Remove(key interface{}) (value interface{}) { } } tree.size-- - value = node.Value + value = node.value return } +// Remove removes the node from the tree by . +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 . +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 @@ -174,9 +301,7 @@ func (tree *RedBlackTree) Size() int { // Keys returns all keys in asc order. func (tree *RedBlackTree) Keys() []interface{} { - tree.mu.RLock() - defer tree.mu.RUnlock() - keys := make([]interface{}, tree.size) + keys := make([]interface{}, tree.Size()) index := 0 tree.IteratorAsc(func(key, value interface{}) bool { keys[index] = key @@ -188,9 +313,7 @@ func (tree *RedBlackTree) Keys() []interface{} { // Values returns all values in asc order based on the key. func (tree *RedBlackTree) Values() []interface{} { - tree.mu.RLock() - defer tree.mu.RUnlock() - values := make([]interface{}, tree.size) + values := make([]interface{}, tree.Size()) index := 0 tree.IteratorAsc(func(key, value interface{}) bool { values[index] = key @@ -202,9 +325,7 @@ func (tree *RedBlackTree) Values() []interface{} { // Map returns all key-value items as map. func (tree *RedBlackTree) Map() map[interface{}]interface{} { - tree.mu.RLock() - defer tree.mu.RUnlock() - m := make(map[interface{}]interface{}, tree.size) + m := make(map[interface{}]interface{}, tree.Size()) tree.IteratorAsc(func(key, value interface{}) bool { m[key] = value return true @@ -249,7 +370,7 @@ func (tree *RedBlackTree) Floor(key interface{}) (floor *RedBlackTreeNode) { found := false node := tree.root for node != nil { - compare := tree.comparator(key, node.Key) + compare := tree.comparator(key, node.key) switch { case compare == 0: return node @@ -277,7 +398,7 @@ func (tree *RedBlackTree) Ceiling(key interface{}) (ceiling *RedBlackTreeNode) { found := false node := tree.root for node != nil { - compare := tree.comparator(key, node.Key) + compare := tree.comparator(key, node.key) switch { case compare == 0: return node @@ -308,7 +429,7 @@ func (tree *RedBlackTree) IteratorAsc(f func (key, value interface{}) bool) { if node == nil { break } - if !f(node.Key, node.Value) { + if !f(node.key, node.value) { break } if node.right != nil { @@ -322,7 +443,7 @@ func (tree *RedBlackTree) IteratorAsc(f func (key, value interface{}) bool) { old := node for node.parent != nil { node = node.parent - if tree.comparator(old.Key, node.Key) <= 0 { + if tree.comparator(old.key, node.key) <= 0 { goto loop } } @@ -345,7 +466,7 @@ func (tree *RedBlackTree) IteratorDesc(f func (key, value interface{}) bool) { if node == nil { break } - if !f(node.Key, node.Value) { + if !f(node.key, node.value) { break } if node.left != nil { @@ -359,7 +480,7 @@ func (tree *RedBlackTree) IteratorDesc(f func (key, value interface{}) bool) { old := node for node.parent != nil { node = node.parent - if tree.comparator(old.Key, node.Key) >= 0 { + if tree.comparator(old.key, node.key) >= 0 { goto loop } } @@ -393,15 +514,41 @@ func (tree *RedBlackTree) Print() { } func (node *RedBlackTreeNode) String() string { - return fmt.Sprintf("%v", node.Key) + return fmt.Sprintf("%v", node.key) } // Search searches the tree with given . -// It returns the node if found or otherwise nil. -func (tree *RedBlackTree) Search(key interface{}) *RedBlackTreeNode { +// Second return parameter 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() - return tree.doSearch(key) + 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 . +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) { @@ -432,19 +579,16 @@ func (tree *RedBlackTree) output(node *RedBlackTreeNode, prefix string, isTail b } } -// Search searches the tree with given . +// Search searches the tree with given 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) + compare := tree.comparator(key, node.key) switch { - case compare == 0: - return node - case compare < 0: - node = node.left - case compare > 0: - node = node.right + case compare == 0: return node + case compare < 0: node = node.left + case compare > 0: node = node.right } } return nil diff --git a/g/container/gvar/gvar.go b/g/container/gvar/gvar.go index acc23dc48..948c9fde7 100644 --- a/g/container/gvar/gvar.go +++ b/g/container/gvar/gvar.go @@ -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, @@ -33,18 +33,6 @@ func New(value interface{}, unsafe...bool) *Var { return v } -// NewRead returns a read-only interface object with given . -// The param used to specify whether using Var in un-concurrent-safety, -// which is false in default, means concurrent-safe. -func NewRead(value interface{}, unsafe...bool) VarRead { - return VarRead(New(value, unsafe...)) -} - -// ReadOnly returns a read-only interface object of . -func (v *Var) ReadOnly() VarRead { - return VarRead(v) -} - // Set sets to , and returns the old value. func (v *Var) Set(value interface{}) (old interface{}) { if v.safe { diff --git a/g/container/gvar/gvar_read.go b/g/container/gvar/gvar_read.go deleted file mode 100644 index 4cb875ef4..000000000 --- a/g/container/gvar/gvar_read.go +++ /dev/null @@ -1,44 +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" -) - -// Read-only interface. -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 -} \ No newline at end of file diff --git a/g/container/gvar/gvar_test.go b/g/container/gvar/gvar_test.go index 8af27577f..68fe77131 100644 --- a/g/container/gvar/gvar_test.go +++ b/g/container/gvar/gvar_test.go @@ -1,3 +1,9 @@ +// 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 ( @@ -9,22 +15,6 @@ import ( "github.com/gogf/gf/g/test/gtest" ) -func TestReadOnly(t *testing.T) { - gtest.Case(t, func() { - obj := gvar.New(nil, true) - var result string - - switch obj.ReadOnly().(type) { - case gvar.VarRead: - result = "yes" - default: - result = "no" - } - - gtest.Assert(result, "yes") - }) -} - func TestSet(t *testing.T) { gtest.Case(t, func() { objOne := gvar.New("old", true) diff --git a/g/internal/cmdenv/cmdenv.go b/g/internal/cmdenv/cmdenv.go index 9dd9a8d08..067310142 100644 --- a/g/internal/cmdenv/cmdenv.go +++ b/g/internal/cmdenv/cmdenv.go @@ -32,7 +32,7 @@ func init() { // 规则: // 1、命令行参数以小写字母格式,使用: gf.包名.变量名 传递; // 2、环境变量参数以大写字母格式,使用: GF_包名_变量名 传递; -func Get(key string, def...interface{}) gvar.VarRead { +func Get(key string, def...interface{}) *gvar.Var { value := interface{}(nil) if len(def) > 0 { value = def[0] diff --git a/g/net/ghttp/ghttp_request.go b/g/net/ghttp/ghttp_request.go index 20ef21458..e6473f4e4 100644 --- a/g/net/ghttp/ghttp_request.go +++ b/g/net/ghttp/ghttp_request.go @@ -76,7 +76,7 @@ func (r *Request) Get(key string, def...interface{}) string { } // 建议都用该参数替代参数获取 -func (r *Request) GetVar(key string, def...interface{}) gvar.VarRead { +func (r *Request) GetVar(key string, def...interface{}) *gvar.Var { return r.GetRequestVar(key, def...) } diff --git a/g/net/ghttp/ghttp_request_params.go b/g/net/ghttp/ghttp_request_params.go index 21a73086e..2277c9cc3 100644 --- a/g/net/ghttp/ghttp_request_params.go +++ b/g/net/ghttp/ghttp_request_params.go @@ -17,7 +17,7 @@ func (r *Request) SetParam(key string, value interface{}) { } // 获取请求流程共享变量 -func (r *Request) GetParam(key string, def...interface{}) gvar.VarRead { +func (r *Request) GetParam(key string, def...interface{}) *gvar.Var { if r.params != nil { if v, ok := r.params[key]; ok { return gvar.New(v, true) diff --git a/g/net/ghttp/ghttp_request_request.go b/g/net/ghttp/ghttp_request_request.go index 32cc6fb8f..f51cd4b79 100644 --- a/g/net/ghttp/ghttp_request_request.go +++ b/g/net/ghttp/ghttp_request_request.go @@ -26,7 +26,7 @@ func (r *Request) GetRequest(key string, def...interface{}) []string { return v } -func (r *Request) GetRequestVar(key string, def...interface{}) gvar.VarRead { +func (r *Request) GetRequestVar(key string, def...interface{}) *gvar.Var { value := r.GetRequest(key, def...) if value != nil { return gvar.New(value[0], true) diff --git a/g/net/ghttp/ghttp_server_session.go b/g/net/ghttp/ghttp_server_session.go index 083a3e37e..496a9d0c3 100644 --- a/g/net/ghttp/ghttp_server_session.go +++ b/g/net/ghttp/ghttp_server_session.go @@ -113,7 +113,7 @@ func (s *Session) Get(key string, def...interface{}) interface{} { } // 获取SESSION,建议都用该方法获取参数 -func (s *Session) GetVar(key string, def...interface{}) gvar.VarRead { +func (s *Session) GetVar(key string, def...interface{}) *gvar.Var { return gvar.NewRead(s.Get(key, def...), true) } diff --git a/g/os/gcfg/gcfg.go b/g/os/gcfg/gcfg.go index 55175a96c..70fcd6833 100644 --- a/g/os/gcfg/gcfg.go +++ b/g/os/gcfg/gcfg.go @@ -296,7 +296,7 @@ func (c *Config) Get(pattern string, def...interface{}) interface{} { return nil } -func (c *Config) GetVar(pattern string, def...interface{}) gvar.VarRead { +func (c *Config) GetVar(pattern string, def...interface{}) *gvar.Var { if j := c.getJson(); j != nil { return gvar.New(j.Get(pattern, def...), true) } diff --git a/g/os/gcmd/gcmd_option.go b/g/os/gcmd/gcmd_option.go index 4cc0147f4..b3865d3fb 100644 --- a/g/os/gcmd/gcmd_option.go +++ b/g/os/gcmd/gcmd_option.go @@ -25,8 +25,8 @@ func (c *gCmdOption) Get(key string, def...string) string { return "" } -// GetVar returns the option value as gvar.VarRead object specified by , +// GetVar returns the option value as *gvar.Var object specified by , // if value does not exist, then returns as its default value. -func (c *gCmdOption) GetVar(key string, def...string) gvar.VarRead { +func (c *gCmdOption) GetVar(key string, def...string) *gvar.Var { return gvar.NewRead(c.Get(key, def...), true) } diff --git a/g/os/gcmd/gcmd_value.go b/g/os/gcmd/gcmd_value.go index b1144b6ac..4792c7614 100644 --- a/g/os/gcmd/gcmd_value.go +++ b/g/os/gcmd/gcmd_value.go @@ -25,8 +25,8 @@ func (c *gCmdValue) Get(index int, def...string) string { return "" } -// GetVar returns value by index as gvar.VarRead object, +// GetVar returns value by index as *gvar.Var object, // if value does not exist, then returns as its default value. -func (c *gCmdValue) GetVar(index int, def...string) gvar.VarRead { +func (c *gCmdValue) GetVar(index int, def...string) *gvar.Var { return gvar.NewRead(c.Get(index, def...), true) } diff --git a/geg/container/gtree/gtree_redblacktree.go b/geg/container/gtree/gtree_redblacktree.go index 660ee446f..58ad1af39 100644 --- a/geg/container/gtree/gtree_redblacktree.go +++ b/geg/container/gtree/gtree_redblacktree.go @@ -1,25 +1,17 @@ package main import ( - "fmt" "github.com/gogf/gf/g/container/gtree" ) func main() { - tree := gtree.New(func(v1, v2 interface{}) int { + tree := gtree.NewRedBlackTree(func(v1, v2 interface{}) int { return v1.(int) - v2.(int) }) for i := 0; i < 20; i++ { - tree.Set(i, i) + tree.Set(i, i*10) } tree.Print() - tree.IteratorAsc(func(key, value interface{}) bool { - fmt.Println(key) - return true - }) - fmt.Println() - tree.IteratorDesc(func(key, value interface{}) bool { - fmt.Println(key) - return true - }) + tree.Flip() + tree.Print() } \ No newline at end of file