diff --git a/container/garray/garray_normal_any.go b/container/garray/garray_normal_any.go index 2248f3f9b..6e71e3e1a 100644 --- a/container/garray/garray_normal_any.go +++ b/container/garray/garray_normal_any.go @@ -604,7 +604,7 @@ func (a *Array) CountValues() map[interface{}]int { return m } -// String returns current array as a string. +// String returns current array as a string, which implements like json.Marshal does. func (a *Array) String() string { a.mu.RLock() defer a.mu.RUnlock() @@ -619,7 +619,7 @@ func (a *Array) String() string { buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`) } if k != len(a.array)-1 { - buffer.WriteString(",") + buffer.WriteByte(',') } } buffer.WriteByte(']') diff --git a/container/garray/garray_normal_int.go b/container/garray/garray_normal_int.go index 99be725ea..eab5ca5b7 100644 --- a/container/garray/garray_normal_int.go +++ b/container/garray/garray_normal_int.go @@ -610,7 +610,7 @@ func (a *IntArray) CountValues() map[int]int { return m } -// String returns current array as a string. +// String returns current array as a string, which implements like json.Marshal does. func (a *IntArray) String() string { return "[" + a.Join(",") + "]" } diff --git a/container/garray/garray_normal_str.go b/container/garray/garray_normal_str.go index 182f1ec93..209e3e266 100644 --- a/container/garray/garray_normal_str.go +++ b/container/garray/garray_normal_str.go @@ -611,7 +611,7 @@ func (a *StrArray) CountValues() map[string]int { return m } -// String returns current array as a string. +// String returns current array as a string, which implements like json.Marshal does. func (a *StrArray) String() string { a.mu.RLock() defer a.mu.RUnlock() @@ -620,7 +620,7 @@ func (a *StrArray) String() string { for k, v := range a.array { buffer.WriteString(`"` + gstr.QuoteMeta(v, `"\`) + `"`) if k != len(a.array)-1 { - buffer.WriteString(",") + buffer.WriteByte(',') } } buffer.WriteByte(']') diff --git a/container/garray/garray_sorted_any.go b/container/garray/garray_sorted_any.go index 6a7d85224..d03fbd638 100644 --- a/container/garray/garray_sorted_any.go +++ b/container/garray/garray_sorted_any.go @@ -549,7 +549,7 @@ func (a *SortedArray) CountValues() map[interface{}]int { return m } -// String returns current array as a string. +// String returns current array as a string, which implements like json.Marshal does. func (a *SortedArray) String() string { a.mu.RLock() defer a.mu.RUnlock() @@ -564,7 +564,7 @@ func (a *SortedArray) String() string { buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`) } if k != len(a.array)-1 { - buffer.WriteString(",") + buffer.WriteByte(',') } } buffer.WriteByte(']') diff --git a/container/garray/garray_sorted_int.go b/container/garray/garray_sorted_int.go index 2497114eb..81ae0ee55 100644 --- a/container/garray/garray_sorted_int.go +++ b/container/garray/garray_sorted_int.go @@ -535,7 +535,7 @@ func (a *SortedIntArray) CountValues() map[int]int { return m } -// String returns current array as a string. +// String returns current array as a string, which implements like json.Marshal does. func (a *SortedIntArray) String() string { return "[" + a.Join(",") + "]" } diff --git a/container/garray/garray_sorted_str.go b/container/garray/garray_sorted_str.go index a8b7ef19a..cc61b1c0e 100644 --- a/container/garray/garray_sorted_str.go +++ b/container/garray/garray_sorted_str.go @@ -536,7 +536,7 @@ func (a *SortedStrArray) CountValues() map[string]int { return m } -// String returns current array as a string. +// String returns current array as a string, which implements like json.Marshal does. func (a *SortedStrArray) String() string { a.mu.RLock() defer a.mu.RUnlock() @@ -545,7 +545,7 @@ func (a *SortedStrArray) String() string { for k, v := range a.array { buffer.WriteString(`"` + gstr.QuoteMeta(v, `"\`) + `"`) if k != len(a.array)-1 { - buffer.WriteString(",") + buffer.WriteByte(',') } } buffer.WriteByte(']') diff --git a/container/gset/gset.go b/container/gset/gset_any_set.go similarity index 82% rename from container/gset/gset.go rename to container/gset/gset_any_set.go index a41903503..9346d6bc6 100644 --- a/container/gset/gset.go +++ b/container/gset/gset_any_set.go @@ -71,6 +71,48 @@ func (set *Set) Add(item ...interface{}) *Set { return set } +// AddIfNotExistFunc adds the returned value of callback function to the set +// if does not exit in the set. +func (set *Set) AddIfNotExistFunc(item interface{}, f func() interface{}) *Set { + if !set.Contains(item) { + set.doAddWithLockCheck(item, f()) + } + return set +} + +// AddIfNotExistFuncLock adds the returned value of callback function to the set +// if does not exit in the set. +// +// Note that the callback function is executed in the mutex.Lock of the set. +func (set *Set) AddIfNotExistFuncLock(item interface{}, f func() interface{}) *Set { + if !set.Contains(item) { + set.doAddWithLockCheck(item, f) + } + return set +} + +// doAddWithLockCheck checks whether item exists with mutex.Lock, +// if not exists, it adds item to the set or else just returns the existing value. +// +// If is type of , +// it will be executed with mutex.Lock of the set, +// and its return value will be added to the set. +// +// It returns item successfully added.. +func (set *Set) doAddWithLockCheck(item interface{}, value interface{}) interface{} { + set.mu.Lock() + defer set.mu.Unlock() + if _, ok := set.data[item]; !ok && value != nil { + if f, ok := value.(func() interface{}); ok { + item = f() + } else { + item = value + } + } + set.data[item] = struct{}{} + return item +} + // Contains checks whether the set contains . func (set *Set) Contains(item interface{}) bool { set.mu.RLock() @@ -121,6 +163,24 @@ func (set *Set) Join(glue string) string { set.mu.RLock() defer set.mu.RUnlock() buffer := bytes.NewBuffer(nil) + l := len(set.data) + i := 0 + for k, _ := range set.data { + buffer.WriteString(gconv.String(k)) + if i != l-1 { + buffer.WriteString(glue) + } + i++ + } + return buffer.String() +} + +// String returns items as a string, which implements like json.Marshal does. +func (set *Set) String() string { + set.mu.RLock() + defer set.mu.RUnlock() + buffer := bytes.NewBuffer(nil) + buffer.WriteByte('[') s := "" l := len(set.data) i := 0 @@ -132,18 +192,14 @@ func (set *Set) Join(glue string) string { buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`) } if i != l-1 { - buffer.WriteString(glue) + buffer.WriteByte(',') } i++ } + buffer.WriteByte(']') return buffer.String() } -// String returns items as a string, which are joined by char ','. -func (set *Set) String() string { - return "[" + set.Join(",") + "]" -} - // LockFunc locks writing with callback function . func (set *Set) LockFunc(f func(m map[interface{}]struct{})) { set.mu.Lock() diff --git a/container/gset/gset_int_set.go b/container/gset/gset_int_set.go index 7b61d7682..c831e1caf 100644 --- a/container/gset/gset_int_set.go +++ b/container/gset/gset_int_set.go @@ -64,6 +64,48 @@ func (set *IntSet) Add(item ...int) *IntSet { return set } +// AddIfNotExistFunc adds the returned value of callback function to the set +// if does not exit in the set. +func (set *IntSet) AddIfNotExistFunc(item int, f func() int) *IntSet { + if !set.Contains(item) { + set.doAddWithLockCheck(item, f()) + } + return set +} + +// AddIfNotExistFuncLock adds the returned value of callback function to the set +// if does not exit in the set. +// +// Note that the callback function is executed in the mutex.Lock of the set. +func (set *IntSet) AddIfNotExistFuncLock(item int, f func() int) *IntSet { + if !set.Contains(item) { + set.doAddWithLockCheck(item, f) + } + return set +} + +// doAddWithLockCheck checks whether item exists with mutex.Lock, +// if not exists, it adds item to the set or else just returns the existing value. +// +// If is type of , +// it will be executed with mutex.Lock of the set, +// and its return value will be added to the set. +// +// It returns item successfully added.. +func (set *IntSet) doAddWithLockCheck(item int, value interface{}) int { + set.mu.Lock() + defer set.mu.Unlock() + if _, ok := set.data[item]; !ok && value != nil { + if f, ok := value.(func() int); ok { + item = f() + } else { + item = value.(int) + } + } + set.data[item] = struct{}{} + return item +} + // Contains checks whether the set contains . func (set *IntSet) Contains(item int) bool { set.mu.RLock() @@ -126,7 +168,7 @@ func (set *IntSet) Join(glue string) string { return buffer.String() } -// String returns items as a string, which are joined by char ','. +// String returns items as a string, which implements like json.Marshal does. func (set *IntSet) String() string { return "[" + set.Join(",") + "]" } diff --git a/container/gset/gset_str_set.go b/container/gset/gset_str_set.go index af391d624..caec5641b 100644 --- a/container/gset/gset_str_set.go +++ b/container/gset/gset_str_set.go @@ -65,6 +65,48 @@ func (set *StrSet) Add(item ...string) *StrSet { return set } +// AddIfNotExistFunc adds the returned value of callback function to the set +// if does not exit in the set. +func (set *StrSet) AddIfNotExistFunc(item string, f func() string) *StrSet { + if !set.Contains(item) { + set.doAddWithLockCheck(item, f()) + } + return set +} + +// AddIfNotExistFuncLock adds the returned value of callback function to the set +// if does not exit in the set. +// +// Note that the callback function is executed in the mutex.Lock of the set. +func (set *StrSet) AddIfNotExistFuncLock(item string, f func() string) *StrSet { + if !set.Contains(item) { + set.doAddWithLockCheck(item, f) + } + return set +} + +// doAddWithLockCheck checks whether item exists with mutex.Lock, +// if not exists, it adds item to the set or else just returns the existing value. +// +// If is type of , +// it will be executed with mutex.Lock of the set, +// and its return value will be added to the set. +// +// It returns item successfully added.. +func (set *StrSet) doAddWithLockCheck(item string, value interface{}) string { + set.mu.Lock() + defer set.mu.Unlock() + if _, ok := set.data[item]; !ok && value != nil { + if f, ok := value.(func() string); ok { + item = f() + } else { + item = value.(string) + } + } + set.data[item] = struct{}{} + return item +} + // Contains checks whether the set contains . func (set *StrSet) Contains(item string) bool { set.mu.RLock() @@ -119,11 +161,7 @@ func (set *StrSet) Join(glue string) string { l := len(set.data) i := 0 for k, _ := range set.data { - if gstr.IsNumeric(k) { - buffer.WriteString(k) - } else { - buffer.WriteString(`"` + gstr.QuoteMeta(k, `"\`) + `"`) - } + buffer.WriteString(k) if i != l-1 { buffer.WriteString(glue) } @@ -132,9 +170,21 @@ func (set *StrSet) Join(glue string) string { return buffer.String() } -// String returns items as a string, which are joined by char ','. +// String returns items as a string, which implements like json.Marshal does. func (set *StrSet) String() string { - return "[" + set.Join(",") + "]" + set.mu.RLock() + defer set.mu.RUnlock() + buffer := bytes.NewBuffer(nil) + l := len(set.data) + i := 0 + for k, _ := range set.data { + buffer.WriteString(`"` + gstr.QuoteMeta(k, `"\`) + `"`) + if i != l-1 { + buffer.WriteByte(',') + } + i++ + } + return buffer.String() } // LockFunc locks writing with callback function . diff --git a/container/gset/gset_z_unit_test.go b/container/gset/gset_z_unit_any_test.go similarity index 87% rename from container/gset/gset_z_unit_test.go rename to container/gset/gset_z_unit_any_test.go index 8663ee523..18dd7b246 100644 --- a/container/gset/gset_z_unit_test.go +++ b/container/gset/gset_z_unit_any_test.go @@ -220,8 +220,8 @@ func TestSet_Join(t *testing.T) { s1 := gset.New(true) s1.Add("a").Add(`"b"`).Add(`\c`) str1 := s1.Join(",") - gtest.Assert(strings.Contains(str1, `\"b\"`), true) - gtest.Assert(strings.Contains(str1, `\\c`), true) + gtest.Assert(strings.Contains(str1, `"b"`), true) + gtest.Assert(strings.Contains(str1, `\c`), true) gtest.Assert(strings.Contains(str1, `a`), true) }) } @@ -305,3 +305,43 @@ func TestSet_Json(t *testing.T) { gtest.Assert(a3.Contains("e"), false) }) } + +func TestSet_AddIfNotExistFunc(t *testing.T) { + gtest.Case(t, func() { + s := gset.New(true) + s.Add(1) + gtest.Assert(s.Contains(1), true) + gtest.Assert(s.Contains(2), false) + + s.AddIfNotExistFunc(2, func() interface{} { + return 3 + }) + gtest.Assert(s.Contains(2), false) + gtest.Assert(s.Contains(3), true) + + s.AddIfNotExistFunc(3, func() interface{} { + return 4 + }) + gtest.Assert(s.Contains(3), true) + gtest.Assert(s.Contains(4), false) + }) + + gtest.Case(t, func() { + s := gset.New(true) + s.Add(1) + gtest.Assert(s.Contains(1), true) + gtest.Assert(s.Contains(2), false) + + s.AddIfNotExistFuncLock(2, func() interface{} { + return 3 + }) + gtest.Assert(s.Contains(2), false) + gtest.Assert(s.Contains(3), true) + + s.AddIfNotExistFuncLock(3, func() interface{} { + return 4 + }) + gtest.Assert(s.Contains(3), true) + gtest.Assert(s.Contains(4), false) + }) +} diff --git a/container/gset/gset_z_unit_int_test.go b/container/gset/gset_z_unit_int_test.go index 03e78e5fc..e15ae5197 100644 --- a/container/gset/gset_z_unit_int_test.go +++ b/container/gset/gset_z_unit_int_test.go @@ -259,3 +259,43 @@ func TestIntSet_Json(t *testing.T) { gtest.Assert(a2.Contains(5), false) }) } + +func TestIntSet_AddIfNotExistFunc(t *testing.T) { + gtest.Case(t, func() { + s := gset.NewIntSet(true) + s.Add(1) + gtest.Assert(s.Contains(1), true) + gtest.Assert(s.Contains(2), false) + + s.AddIfNotExistFunc(2, func() int { + return 3 + }) + gtest.Assert(s.Contains(2), false) + gtest.Assert(s.Contains(3), true) + + s.AddIfNotExistFunc(3, func() int { + return 4 + }) + gtest.Assert(s.Contains(3), true) + gtest.Assert(s.Contains(4), false) + }) + + gtest.Case(t, func() { + s := gset.NewIntSet(true) + s.Add(1) + gtest.Assert(s.Contains(1), true) + gtest.Assert(s.Contains(2), false) + + s.AddIfNotExistFuncLock(2, func() int { + return 3 + }) + gtest.Assert(s.Contains(2), false) + gtest.Assert(s.Contains(3), true) + + s.AddIfNotExistFuncLock(3, func() int { + return 4 + }) + gtest.Assert(s.Contains(3), true) + gtest.Assert(s.Contains(4), false) + }) +} diff --git a/container/gset/gset_z_unit_str_test.go b/container/gset/gset_z_unit_str_test.go index 23689ec28..45c271991 100644 --- a/container/gset/gset_z_unit_str_test.go +++ b/container/gset/gset_z_unit_str_test.go @@ -207,8 +207,8 @@ func TestStrSet_Join(t *testing.T) { s1 := gset.NewStrSet() s1.Add("a").Add(`"b"`).Add(`\c`) str1 := s1.Join(",") - gtest.Assert(strings.Contains(str1, `\"b\"`), true) - gtest.Assert(strings.Contains(str1, `\\c`), true) + gtest.Assert(strings.Contains(str1, `"b"`), true) + gtest.Assert(strings.Contains(str1, `\c`), true) gtest.Assert(strings.Contains(str1, `a`), true) }) } @@ -304,3 +304,43 @@ func TestStrSet_Json(t *testing.T) { gtest.Assert(a3.Contains("e"), false) }) } + +func TestStrSet_AddIfNotExistFunc(t *testing.T) { + gtest.Case(t, func() { + s := gset.NewStrSet(true) + s.Add("1") + gtest.Assert(s.Contains("1"), true) + gtest.Assert(s.Contains("2"), false) + + s.AddIfNotExistFunc("2", func() string { + return "3" + }) + gtest.Assert(s.Contains("2"), false) + gtest.Assert(s.Contains("3"), true) + + s.AddIfNotExistFunc("3", func() string { + return "4" + }) + gtest.Assert(s.Contains("3"), true) + gtest.Assert(s.Contains("4"), false) + }) + + gtest.Case(t, func() { + s := gset.NewStrSet(true) + s.Add("1") + gtest.Assert(s.Contains("1"), true) + gtest.Assert(s.Contains("2"), false) + + s.AddIfNotExistFuncLock("2", func() string { + return "3" + }) + gtest.Assert(s.Contains("2"), false) + gtest.Assert(s.Contains("3"), true) + + s.AddIfNotExistFuncLock("3", func() string { + return "4" + }) + gtest.Assert(s.Contains("3"), true) + gtest.Assert(s.Contains("4"), false) + }) +}