Skip to content

Commit 3d1aafc

Browse files
author
murad
committed
Rewrite the package using Generics and add Module support
Signed-off-by: murad <[email protected]>
1 parent dbe832a commit 3d1aafc

File tree

6 files changed

+100
-145
lines changed

6 files changed

+100
-145
lines changed

example_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package ordered_sync_map_test
2+
3+
import (
4+
"fmt"
5+
6+
ordered_sync_map "github.com/m-murad/ordered-sync-map"
7+
)
8+
9+
func ExampleNew() {
10+
11+
mp := ordered_sync_map.New[string, string]()
12+
13+
mp.Put("k1", "v1")
14+
15+
v, ok := mp.Get("k1")
16+
fmt.Println(v, ok)
17+
18+
ok = mp.Delete("k2")
19+
fmt.Println(ok)
20+
21+
mp.UnorderedRange(func(key, value string) {
22+
fmt.Println(key, value)
23+
})
24+
25+
mp.OrderedRange(func(key, value string) {
26+
fmt.Println(key, value)
27+
})
28+
29+
len := mp.Length()
30+
fmt.Println(len)
31+
32+
v, ok = mp.GetOrPut("k1", "v2")
33+
fmt.Println(v, ok)
34+
35+
v, ok = mp.GetAndDelete("k1")
36+
fmt.Println(v, ok)
37+
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/m-murad/ordered-sync-map
2+
3+
go 1.20

map.go

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,59 +5,61 @@ import (
55
"sync"
66
)
77

8-
type mapElement struct {
9-
key interface{}
10-
value interface{}
8+
type mapElement[K comparable, V any] struct {
9+
key K
10+
value V
1111
}
1212

1313
// Map is a thread safe and ordered implementation of standard map.
14-
type Map struct {
15-
mp map[interface{}]*list.Element
14+
// K is the type of key and V is the type of value.
15+
type Map[K comparable, V any] struct {
16+
mp map[K]*list.Element
1617
mu sync.RWMutex
1718
dll *list.List
1819
}
1920

20-
// New returns an initialized Map.
21-
func New() *Map {
22-
m := new(Map)
23-
m.mp = make(map[interface{}]*list.Element)
21+
// New returns an initialized Map[K, V].
22+
func New[K comparable, V any]() *Map[K, V] {
23+
m := new(Map[K, V])
24+
m.mp = make(map[K]*list.Element)
2425
m.dll = list.New()
2526
return m
2627
}
2728

28-
// Get returns the value stored in the map for a key, or nil if no
29-
// value is present.
30-
// The ok result indicates whether value was found in the map.
31-
func (m *Map) Get(key interface{}) (interface{}, bool) {
29+
// Get returns the value stored in the map for a key.
30+
// If the key is not found in the Map it return the zero value of type V.
31+
// The bool indicates whether value was found in the map.
32+
func (m *Map[K, V]) Get(key K) (V, bool) {
3233
m.mu.RLock()
3334
defer m.mu.RUnlock()
3435

3536
v, ok := m.mp[key]
3637
if !ok {
37-
return nil, false
38+
var value V
39+
return value, ok
3840
}
3941

40-
me := v.Value.(mapElement)
42+
me := v.Value.(mapElement[K, V])
4143
return me.value, ok
4244
}
4345

4446
// Put sets the value for the given key.
4547
// It will replace the value if the key already exists in the map
4648
// even if the values are same.
47-
func (m *Map) Put(key interface{}, val interface{}) {
49+
func (m *Map[K, V]) Put(key K, val V) {
4850
m.mu.Lock()
4951
defer m.mu.Unlock()
5052

5153
if e, ok := m.mp[key]; !ok {
52-
m.mp[key] = m.dll.PushFront(mapElement{key: key, value: val})
54+
m.mp[key] = m.dll.PushFront(mapElement[K, V]{key: key, value: val})
5355
} else {
54-
e.Value = mapElement{key: key, value: val}
56+
e.Value = mapElement[K, V]{key: key, value: val}
5557
}
5658
}
5759

5860
// Delete deletes the value for a key.
5961
// It returns a boolean indicating weather the key existed and it was deleted.
60-
func (m *Map) Delete(key interface{}) bool {
62+
func (m *Map[K, V]) Delete(key K) bool {
6163
m.mu.Lock()
6264
defer m.mu.Unlock()
6365

@@ -75,35 +77,32 @@ func (m *Map) Delete(key interface{}) bool {
7577
// This is same as ranging over a map using the "for range" syntax.
7678
// Parameter func f should not call any method of the Map, eg Get, Put, Delete, UnorderedRange, OrderedRange etc
7779
// It will cause a deadlock.
78-
func (m *Map) UnorderedRange(f func(key interface{}, value interface{})) {
80+
func (m *Map[K, V]) UnorderedRange(f func(key K, value V)) {
7981
m.mu.RLock()
8082
defer m.mu.RUnlock()
8183

8284
for k, v := range m.mp {
83-
f(k, v.Value.(mapElement).value)
85+
f(k, v.Value.(mapElement[K, V]).value)
8486
}
8587
}
8688

8789
// OrderedRange will range over the map in ab ordered sequence.
88-
// This is way faster than UnorderedRange. For a map containing 10_000_000 items
89-
// UnorderedRange completes in ~1.7 seconds,
90-
// OrderedRange completes in ~98 milli seconds.
9190
// Parameter func f should not call any method of the Map, eg Get, Put, Delete, UnorderedRange, OrderedRange etc
9291
// It will cause a deadlock.
93-
func (m *Map) OrderedRange(f func(key interface{}, value interface{})) {
92+
func (m *Map[K, V]) OrderedRange(f func(key K, value V)) {
9493
m.mu.RLock()
9594
defer m.mu.RUnlock()
9695

9796
cur := m.dll.Back()
9897
for cur != nil {
99-
me := cur.Value.(mapElement)
98+
me := cur.Value.(mapElement[K, V])
10099
f(me.key, me.value)
101100
cur = cur.Prev()
102101
}
103102
}
104103

105104
// Length will return the length of Map.
106-
func (m *Map) Length() int {
105+
func (m *Map[k, V]) Length() int {
107106
m.mu.RLock()
108107
defer m.mu.RUnlock()
109108

@@ -114,31 +113,33 @@ func (m *Map) Length() int {
114113
// If the key did not exist previously it will be added to the Map.
115114
// updated will be true if the key existed previously
116115
// otherwise it will be false if the key did not exist and was added to the Map.
117-
func (m *Map) GetOrPut(key interface{}, value interface{}) (finalValue interface{}, updated bool) {
116+
func (m *Map[K, V]) GetOrPut(key K, value V) (V, bool) {
118117
m.mu.Lock()
119118
defer m.mu.Unlock()
120119

121120
if e, exists := m.mp[key]; exists {
122-
e.Value = mapElement{key: key, value: value}
123-
return value, true
121+
me := e.Value.(mapElement[K, V])
122+
return me.value, true
124123
} else {
125-
m.mp[key] = m.dll.PushFront(mapElement{key: key, value: value})
124+
m.mp[key] = m.dll.PushFront(mapElement[K, V]{key: key, value: value})
126125
return value, false
127126
}
128127
}
129128

130129
// GetAndDelete will get the value saved against the given key.
131130
// deleted will be true if the key existed previously
132131
// otherwise it will be false.
133-
func (m *Map) GetAndDelete(key interface{}) (value interface{}, deleted bool) {
132+
func (m *Map[K, V]) GetAndDelete(key K) (V, bool) {
134133
m.mu.Lock()
135134
defer m.mu.Unlock()
136135

137136
if e, exists := m.mp[key]; exists {
138137
m.dll.Remove(e)
139138
delete(m.mp, key)
140-
return e.Value, true
139+
me := e.Value.(mapElement[K, V])
140+
return me.value, true
141141
} else {
142-
return nil, false
142+
var value V
143+
return value, false
143144
}
144145
}

map_benchmark_test.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@ package ordered_sync_map_test
22

33
import (
44
"fmt"
5-
mp "github.com/m-murad/ordered-sync-map"
65
"testing"
6+
7+
mp "github.com/m-murad/ordered-sync-map"
78
)
89

9-
func getPopulatedOrderedSyncMap(size int) *mp.Map {
10-
m := mp.New()
10+
func getPopulatedOrderedSyncMap(size int) *mp.Map[any, any] {
11+
m := mp.New[any, any]()
1112
populateOrderedSyncMap(m, size)
1213
return m
1314
}
1415

15-
func populateOrderedSyncMap(m *mp.Map, size int) {
16+
func populateOrderedSyncMap(m *mp.Map[any, any], size int) {
1617
for i := 0; i < size; i++ {
1718
m.Put(i, i)
1819
}
@@ -33,7 +34,7 @@ func BenchmarkOrderedSyncMapGet(b *testing.B) {
3334

3435
func BenchmarkOrderedSyncMapPut(b *testing.B) {
3536
for n := 1; n <= 10; n++ {
36-
m := mp.New()
37+
m := mp.New[any, any]()
3738
b.Run(fmt.Sprintf("Put in ordered_sync_map - %d", n), func(b *testing.B) {
3839
populateOrderedSyncMap(m, b.N)
3940
})

map_test.go

Lines changed: 18 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package ordered_sync_map_test
22

33
import (
4-
mp "github.com/m-murad/ordered-sync-map"
54
"testing"
5+
6+
mp "github.com/m-murad/ordered-sync-map"
67
)
78

8-
func initMap() *mp.Map {
9-
return mp.New()
9+
func initMap() *mp.Map[any, any] {
10+
return mp.New[any, any]()
1011
}
1112

1213
func TestGetPutDelete(t *testing.T) {
@@ -102,8 +103,8 @@ func TestOrderedRange(t *testing.T) {
102103
{123, "key", "val 1", "val_2", true}, //values
103104
}
104105

105-
m := mp.New()
106-
for i, _ := range kvs[0] {
106+
m := initMap()
107+
for i := range kvs[0] {
107108
m.Put(kvs[0][i], kvs[1][i])
108109
}
109110

@@ -123,91 +124,48 @@ func TestOrderedRange(t *testing.T) {
123124
func TestLength(t *testing.T) {
124125
m := initMap()
125126

126-
m.Put("a", 1)
127-
m.Put("b", 2)
128-
if m.Length() != 2 {
129-
t.FailNow()
130-
}
131-
132-
m.Put("c", 2)
133-
m.Put("d", 2)
134-
m.Put("e", 2)
135-
if m.Length() != 5 {
127+
if m.Length() != 0 {
136128
t.FailNow()
137129
}
138130

139-
m.Put("e", 3)
140-
if m.Length() != 5 {
131+
m.Put("a", 1)
132+
m.Put("b", 2)
133+
if m.Length() != 2 {
141134
t.FailNow()
142135
}
143136

144137
m.Delete("a")
145-
if m.Length() != 4 {
138+
if m.Length() != 1 {
146139
t.FailNow()
147140
}
148141

149142
m.Delete("does_not_exist")
150-
if m.Length() != 4 {
151-
t.FailNow()
152-
}
153-
154-
m.Delete("b")
155-
m.Delete("c")
156-
m.Delete("d")
157143
if m.Length() != 1 {
158144
t.FailNow()
159145
}
160-
161-
m.Delete("e")
162-
if m.Length() != 0 {
163-
t.FailNow()
164-
}
165146
}
166147

167148
func TestGetOrPut(t *testing.T) {
168149
m := initMap()
169150

170-
m.Put("a", 1)
171-
m.Put("b", 2)
172-
m.Put("c", 3)
173-
174-
if finalValue, updated := m.GetOrPut("a", 4); finalValue != 1 && !updated {
175-
t.FailNow()
176-
}
177-
178-
if finalValue, updated := m.GetOrPut("d", 4); finalValue != 4 && updated {
179-
t.FailNow()
180-
}
181-
182-
m.Delete("a")
183-
if finalValue, updated := m.GetOrPut("a", 5); finalValue != 5 && updated {
184-
t.FailNow()
151+
if finalValue, updated := m.GetOrPut("a", 5); finalValue != 5 || updated {
152+
t.Fail()
185153
}
186154

187-
m.Put("e", 5)
188-
if finalValue, updated := m.GetOrPut("e", 6); finalValue != 5 && !updated {
189-
t.FailNow()
155+
if finalValue, updated := m.GetOrPut("a", 4); finalValue != 5 || !updated {
156+
t.Fail()
190157
}
191158
}
192159

193160
func TestGetAndDelete(t *testing.T) {
194161
m := initMap()
195162

196-
m.Put("a", 1)
197-
m.Put("b", 2)
198-
m.Put("c", 3)
199-
m.Put("d", 4)
200-
201-
if value, deleted := m.GetAndDelete("a"); value != 1 && !deleted {
202-
t.Fail()
203-
}
204-
205-
if value, deleted := m.GetAndDelete("a"); value != nil && deleted {
163+
if value, deleted := m.GetAndDelete("a"); value != nil || deleted {
206164
t.Fail()
207165
}
208166

209-
m.Put("a", 5)
210-
if value, deleted := m.GetAndDelete("a"); value != 5 && !deleted {
167+
m.Put("a", 2)
168+
if value, deleted := m.GetAndDelete("a"); value != 2 || !deleted {
211169
t.Fail()
212170
}
213171
}

0 commit comments

Comments
 (0)