-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathbimap.go
155 lines (129 loc) · 3.49 KB
/
bimap.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Package bimap provides a threadsafe bidirectional map
package bimap
import "sync"
// BiMap is a bi-directional hashmap that is thread safe and supports immutability
type BiMap[K comparable, V comparable] struct {
s sync.RWMutex
immutable bool
forward map[K]V
inverse map[V]K
}
// NewBiMap returns a an empty, mutable, biMap
func NewBiMap[K comparable, V comparable]() *BiMap[K, V] {
return &BiMap[K, V]{forward: make(map[K]V), inverse: make(map[V]K), immutable: false}
}
// NewBiMapFrom returns a new BiMap from a map[K, V]
func NewBiMapFromMap[K comparable, V comparable](forwardMap map[K]V) *BiMap[K, V] {
biMap := NewBiMap[K, V]()
for k, v := range forwardMap {
biMap.Insert(k, v)
}
return biMap
}
// Insert puts a key and value into the BiMap, provided its mutable. Also creates the reverse mapping from value to key.
func (b *BiMap[K, V]) Insert(k K, v V) {
b.s.RLock()
if b.immutable {
panic("Cannot modify immutable map")
}
b.s.RUnlock()
b.s.Lock()
defer b.s.Unlock()
if _, ok := b.forward[k]; ok {
delete(b.inverse, b.forward[k])
}
b.forward[k] = v
b.inverse[v] = k
}
// Exists checks whether or not a key exists in the BiMap
func (b *BiMap[K, V]) Exists(k K) bool {
b.s.RLock()
defer b.s.RUnlock()
_, ok := b.forward[k]
return ok
}
// ExistsInverse checks whether or not a value exists in the BiMap
func (b *BiMap[K, V]) ExistsInverse(k V) bool {
b.s.RLock()
defer b.s.RUnlock()
_, ok := b.inverse[k]
return ok
}
// Get returns the value for a given key in the BiMap and whether or not the element was present.
func (b *BiMap[K, V]) Get(k K) (V, bool) {
if !b.Exists(k) {
return *new(V), false
}
b.s.RLock()
defer b.s.RUnlock()
return b.forward[k], true
}
// GetInverse returns the key for a given value in the BiMap and whether or not the element was present.
func (b *BiMap[K, V]) GetInverse(v V) (K, bool) {
if !b.ExistsInverse(v) {
return *new(K), false
}
b.s.RLock()
defer b.s.RUnlock()
return b.inverse[v], true
}
// Delete removes a key-value pair from the BiMap for a given key. Returns if the key doesn't exist
func (b *BiMap[K, V]) Delete(k K) {
b.s.RLock()
if b.immutable {
panic("Cannot modify immutable map")
}
b.s.RUnlock()
if !b.Exists(k) {
return
}
val, _ := b.Get(k)
b.s.Lock()
defer b.s.Unlock()
delete(b.forward, k)
delete(b.inverse, val)
}
// DeleteInverse emoves a key-value pair from the BiMap for a given value. Returns if the value doesn't exist
func (b *BiMap[K, V]) DeleteInverse(v V) {
b.s.RLock()
if b.immutable {
panic("Cannot modify immutable map")
}
b.s.RUnlock()
if !b.ExistsInverse(v) {
return
}
key, _ := b.GetInverse(v)
b.s.Lock()
defer b.s.Unlock()
delete(b.inverse, v)
delete(b.forward, key)
}
// Size returns the number of elements in the bimap
func (b *BiMap[K, V]) Size() int {
b.s.RLock()
defer b.s.RUnlock()
return len(b.forward)
}
// MakeImmutable freezes the BiMap preventing any further write actions from taking place
func (b *BiMap[K, V]) MakeImmutable() {
b.s.Lock()
defer b.s.Unlock()
b.immutable = true
}
// GetInverseMap returns a regular go map mapping from the BiMap's values to its keys
func (b *BiMap[K, V]) GetInverseMap() map[V]K {
return b.inverse
}
// GetForwardMap returns a regular go map mapping from the BiMap's keys to its values
func (b *BiMap[K, V]) GetForwardMap() map[K]V {
return b.forward
}
// Lock manually locks the BiMap's mutex
func (b *BiMap[K, V]) Lock() {
b.s.Lock()
}
// Unlock manually unlocks the BiMap's mutex
func (b *BiMap[K, V]) Unlock() {
b.s.Unlock()
}