diff --git a/maps/concurrent_map.go b/maps/concurrent_map.go new file mode 100644 index 0000000..7af9b02 --- /dev/null +++ b/maps/concurrent_map.go @@ -0,0 +1,131 @@ +package maps + +import ( + "fmt" + "runtime" + "sync" + + "github.com/charlienet/go-mixed/bytesconv" + "github.com/charlienet/go-mixed/hash" + "golang.org/x/exp/constraints" +) + +var defaultNumOfBuckets = runtime.GOMAXPROCS(runtime.NumCPU()) + +type ConcurrnetMap[K constraints.Ordered, V any] struct { + buckets []Map[K, V] + numOfBuckets uint64 +} + +func NewConcurrentMap[K constraints.Ordered, V any]() *ConcurrnetMap[K, V] { + num := defaultNumOfBuckets + + buckets := make([]Map[K, V], num) + for i := 0; i < num; i++ { + buckets[i] = NewRWMap[K, V]() + } + + return &ConcurrnetMap[K, V]{ + numOfBuckets: uint64(num), + buckets: buckets, + } +} + +func (m *ConcurrnetMap[K, V]) Set(key K, value V) { + m.getBucket(key).Set(key, value) +} + +func (m *ConcurrnetMap[K, V]) Get(key K) (V, bool) { + return m.getBucket(key).Get(key) +} + +func (m *ConcurrnetMap[K, V]) Delete(key K) { + im := m.getBucket(key) + im.Delete(key) +} + +func (m *ConcurrnetMap[K, V]) Exist(key K) bool { + mm := m.getBucket(key) + return mm.Exist(key) +} + +func (m *ConcurrnetMap[K, V]) ForEach(f func(K, V)) { + var wg sync.WaitGroup + + num := int(m.numOfBuckets) + + wg.Add(int(m.numOfBuckets)) + for i := 0; i < num; i++ { + go func(i int) { + m.buckets[i].ForEach(f) + wg.Done() + }(i) + } + + wg.Wait() +} + +func (m *ConcurrnetMap[K, V]) Clone() Map[K, V] { + + num := int(m.numOfBuckets) + + buckets := make([]Map[K, V], m.numOfBuckets) + for i := 0; i < num; i++ { + buckets[i] = m.buckets[i].Clone() + } + + return &ConcurrnetMap[K, V]{ + buckets: buckets, + numOfBuckets: m.numOfBuckets, + } +} + +func (m *ConcurrnetMap[K, V]) Clear() { + for i := 0; i < int(m.numOfBuckets); i++ { + m.buckets[i].Clear() + } +} + +func (m *ConcurrnetMap[K, V]) Count() int { + var count int + for i := 0; i < int(m.numOfBuckets); i++ { + count += m.buckets[i].Count() + } + + return count +} + +func (m *ConcurrnetMap[K, V]) getBucket(k K) Map[K, V] { + id := getTag(k) % m.numOfBuckets + return m.buckets[id] +} + +func getTag[T comparable](v T) uint64 { + var vv any = v + + switch vv.(type) { + case string: + return fnv(vv.(string)) + case int8: + return uint64(vv.(int8)) + case uint8: + return uint64(vv.(uint8)) + case int: + return uint64(vv.(int)) + case int32: + return uint64(vv.(int32)) + case uint32: + return uint64(vv.(uint32)) + case int64: + return uint64(vv.(int64)) + case uint64: + return vv.(uint64) + default: + return fnv(fmt.Sprintf("%v", v)) + } +} + +func fnv(k string) uint64 { + bytes := bytesconv.StringToBytes(k) + return uint64(hash.Funv32(bytes)) +} diff --git a/maps/hash_map.go b/maps/hash_map.go new file mode 100644 index 0000000..8b14fb1 --- /dev/null +++ b/maps/hash_map.go @@ -0,0 +1,58 @@ +package maps + +import ( + "golang.org/x/exp/constraints" +) + +type hashMap[K constraints.Ordered, V any] struct { + m map[K]V +} + +func NewHashMap[K constraints.Ordered, V any]() *hashMap[K, V] { + return &hashMap[K, V]{m: make(map[K]V)} +} + +func newHashMap[K constraints.Ordered, V any](m map[K]V) *hashMap[K, V] { + return &hashMap[K, V]{m: m} +} + +func (m *hashMap[K, V]) Set(key K, value V) { + m.m[key] = value +} + +func (m *hashMap[K, V]) Get(key K) (V, bool) { + v, exist := m.m[key] + return v, exist +} + +func (m *hashMap[K, V]) Delete(key K) { + delete(m.m, key) +} + +func (m *hashMap[K, V]) Exist(key K) bool { + _, ok := m.m[key] + return ok +} + +func (m *hashMap[K, V]) ForEach(f func(K, V)) { + for k, v := range m.m { + f(k, v) + } +} + +func (m *hashMap[K, V]) Clear() { + m.m = make(map[K]V) +} + +func (m *hashMap[K, V]) Count() int { + return len(m.m) +} + +func (m *hashMap[K, V]) Clone() Map[K, V] { + r := make(map[K]V, len(m.m)) + for k, v := range m.m { + r[k] = v + } + + return newHashMap(r) +} diff --git a/maps/linked_map.go b/maps/linked_map.go new file mode 100644 index 0000000..3470f7b --- /dev/null +++ b/maps/linked_map.go @@ -0,0 +1 @@ +package maps diff --git a/maps/map.go b/maps/map.go new file mode 100644 index 0000000..340eefa --- /dev/null +++ b/maps/map.go @@ -0,0 +1,14 @@ +package maps + +import "golang.org/x/exp/constraints" + +type Map[K constraints.Ordered, V any] interface { + Set(key K, value V) + Get(key K) (value V, ok bool) + Exist(key K) bool + Delete(key K) + Clone() Map[K, V] + Clear() + Count() int + ForEach(f func(K, V)) +} diff --git a/maps/map_test.go b/maps/map_test.go new file mode 100644 index 0000000..6119f26 --- /dev/null +++ b/maps/map_test.go @@ -0,0 +1,18 @@ +package maps_test + +import ( + "testing" + + "github.com/charlienet/go-mixed/maps" +) + +func TestMap(t *testing.T) { + _ = maps.NewHashMap[string, any]() +} + +func TestHashMap(t *testing.T) { + var m maps.Map[string, any] = maps.NewHashMap[string, any]() + + _ = m + +} diff --git a/maps/rwlock_map.go b/maps/rwlock_map.go new file mode 100644 index 0000000..7bdffd7 --- /dev/null +++ b/maps/rwlock_map.go @@ -0,0 +1,67 @@ +package maps + +import ( + "sync" + + "golang.org/x/exp/constraints" +) + +var _ Map[string, any] = &rw_map[string, any]{} + +type rw_map[K constraints.Ordered, V any] struct { + m Map[K, V] + mu sync.RWMutex +} + +func NewRWMap[K constraints.Ordered, V any]() *rw_map[K, V] { + return &rw_map[K, V]{} +} + +func newRWMap[K constraints.Ordered, V any](m Map[K, V]) *rw_map[K, V] { + return &rw_map[K, V]{m: m} +} + +func (m *rw_map[K, V]) Set(key K, value V) { + m.mu.Lock() + m.m.Set(key, value) + m.mu.Unlock() +} + +func (m *rw_map[K, V]) Get(key K) (V, bool) { + m.mu.RLock() + defer m.mu.RUnlock() + + return m.m.Get(key) +} + +func (m *rw_map[K, V]) Delete(key K) { + m.mu.RLock() + defer m.mu.RUnlock() + + m.m.Delete(key) +} + +func (m *rw_map[K, V]) Exist(key K) bool { + return m.m.Exist(key) +} + +func (m *rw_map[K, V]) Count() int { + return m.m.Count() +} + +func (m *rw_map[K, V]) ForEach(f func(K, V)) { + m.mu.RLock() + defer m.mu.RUnlock() + + m.m.ForEach(f) +} + +func (m *rw_map[K, V]) Clone() Map[K, V] { + return newRWMap(m.m.Clone()) +} + +func (m *rw_map[K, V]) Clear() { + m.mu.Lock() + m.m.Clear() + m.mu.Unlock() +} diff --git a/maps/sort_map.go b/maps/sort_map.go new file mode 100644 index 0000000..69f6c41 --- /dev/null +++ b/maps/sort_map.go @@ -0,0 +1,136 @@ +package maps + +import ( + "fmt" + "strings" + + "golang.org/x/exp/constraints" + "golang.org/x/exp/slices" +) + +var ( + _ Map[string, any] = &sorted_map[string, any]{} + _ SortedMap[string, any] = &sorted_map[string, any]{} +) + +type SortedMap[K constraints.Ordered, V any] interface { + Asc() SortedMap[K, V] + Desc() SortedMap[K, V] +} + +type sorted_map[K constraints.Ordered, V any] struct { + keys []K + maps Map[K, V] +} + +func NewSortedMap[K constraints.Ordered, V any]() *sorted_map[K, V] { + return &sorted_map[K, V]{keys: make([]K, 0), maps: NewHashMap[K, V]()} +} + +func NewSortedByMap[K constraints.Ordered, V any](m Map[K, V]) *sorted_map[K, V] { + return &sorted_map[K, V]{maps: m, keys: getKeys(m)} +} + +func (m *sorted_map[K, V]) Get(key K) (V, bool) { + return m.Get(key) +} + +func (m *sorted_map[K, V]) Set(key K, value V) { + m.Set(key, value) + m.keys = append(m.keys, key) +} + +func (m *sorted_map[K, V]) Delete(key K) { + m.maps.Delete(key) + + for index := range m.keys { + if m.keys[index] == key { + m.keys = append(m.keys[:index], m.keys[index+1:]...) + break + } + } +} + +func (m *sorted_map[K, V]) Count() int { + return m.maps.Count() +} + +func (m *sorted_map[K, V]) Clear() { + m.keys = make([]K, 0) + m.maps.Clear() +} + +func (m *sorted_map[K, V]) Clone() Map[K, V] { + return &sorted_map[K, V]{maps: m.maps.Clone(), keys: getKeys(m.maps)} +} + +func (m *sorted_map[K, V]) ForEach(f func(K, V)) { + m.maps.ForEach(f) +} + +func (m *sorted_map[K, V]) Exist(key K) bool { + return m.Exist(key) +} + +func (m *sorted_map[K, V]) Join(sep string, f func(k K, v V) string) string { + slice := make([]string, 0, m.maps.Count()) + for _, k := range m.keys { + v, _ := m.maps.Get(k) + slice = append(slice, f(k, v)) + } + + return strings.Join(slice, sep) +} + +func (m *sorted_map[K, V]) Keys() []K { + return m.keys +} + +func (s *sorted_map[K, V]) Values() []V { + ret := make([]V, 0, s.maps.Count()) + for _, k := range s.keys { + v, _ := s.maps.Get(k) + ret = append(ret, v) + } + + return ret +} + +func (m *sorted_map[K, V]) String() string { + return fmt.Sprintf("map[%s]", m.Join(" ", func(k K, v V) string { + return fmt.Sprintf("%v:%v", k, v) + })) +} + +func (m *sorted_map[K, V]) Asc() SortedMap[K, V] { + keys := m.keys + slices.Sort(keys) + + return &sorted_map[K, V]{ + maps: m.maps, + keys: keys, + } +} + +func (m *sorted_map[K, V]) Desc() SortedMap[K, V] { + keys := m.keys + + slices.SortFunc(keys, func(a, b K) bool { + return a > b + }) + + return &sorted_map[K, V]{ + maps: m.maps, + keys: keys, + } +} + +func getKeys[K constraints.Ordered, V any](m Map[K, V]) []K { + keys := make([]K, 0, m.Count()) + + m.ForEach(func(k K, v V) { + keys = append(keys, k) + }) + + return keys +} diff --git a/maps/tree_map.go b/maps/tree_map.go new file mode 100644 index 0000000..3470f7b --- /dev/null +++ b/maps/tree_map.go @@ -0,0 +1 @@ +package maps diff --git a/readme.md b/readme.md index ec89461..1ad06ba 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,3 @@ -杂物工具库 +# 杂物工具库 go >= 1.18 - -