github.com/Nigel2392/go-datastructures@v1.1.5/hashmap/hashmap.go (about) 1 package hashmap 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 8 "github.com/Nigel2392/go-datastructures" 9 ) 10 11 const defaultBucketLen = 16 12 13 // Not to be used directly. Use the Map() function instead. 14 // 15 // A hashmap implementation with a specified number of buckets. 16 // 17 // These buckets are implemented as binary search trees. 18 // 19 // Inside the binary search trees, the keys are stored as linked lists. 20 // 21 // It is up to the user to ensure that the key type implements the datastructures.Hashable[T] interface, 22 // 23 // and that the key hashing function is secure, fast and collision-free. 24 type HashMap[T1 datastructures.Hashable[T1], T2 any] struct { 25 buckets []*bucket[T1, T2] 26 len int 27 bucketLen uint64 28 } 29 30 // Returns a new HashMap[T1, T2]. 31 // 32 // If no argument is given, the default number of buckets is used (16). 33 func Map[T1 datastructures.Hashable[T1], T2 any](amount ...int) *HashMap[T1, T2] { 34 if len(amount) == 0 { 35 return newMap[T1, T2](defaultBucketLen) 36 } else if len(amount) > 1 { 37 panic(fmt.Sprintf("Map[T1, T2] takes at most 1 argument, %d given", len(amount))) 38 } 39 var k = amount[0] 40 if k < 0 { 41 panic(fmt.Sprintf("Map[T1, T2] takes a positive integer, %d given", amount)) 42 } 43 if k == 0 { 44 return newMap[T1, T2](defaultBucketLen) 45 } 46 47 return newMap[T1, T2](calcBuckets(uint64(k))) 48 } 49 50 // Calculates the number of buckets to use for the given number of items. 51 // 52 // The number of buckets is a power of 2. 53 func calcBuckets(items uint64) uint64 { 54 if items > (1 << 31) { 55 return uint64(float64(items) * 1.5) 56 } 57 58 var buckets uint64 = 1 59 for buckets < items { 60 buckets <<= 1 61 } 62 63 if buckets > (1 << 31) { 64 return uint64(float64(buckets>>1) * 1.5) 65 } 66 67 return buckets 68 } 69 70 // Instantiates a new HashMap[T1, T2] with the given number of buckets. 71 func newMap[T1 datastructures.Hashable[T1], T2 any](buckets uint64) *HashMap[T1, T2] { 72 var table = HashMap[T1, T2]{ 73 bucketLen: buckets, 74 buckets: make([]*bucket[T1, T2], buckets, buckets), 75 } 76 for i := range table.buckets { 77 table.buckets[i] = &bucket[T1, T2]{} 78 } 79 return &table 80 } 81 82 func indexOf(hash uint64, buckets uint64) uint64 { 83 return (hash ^ (hash >> 16)) & (buckets - 1) 84 } 85 86 // Sets a value in the map. 87 func (t *HashMap[T1, T2]) Set(k T1, v T2) { 88 var hash uint64 = k.Hash() 89 t.buckets[indexOf(hash, t.bucketLen)].insert(hash, k, v) 90 t.len++ 91 } 92 93 // Gets a value from the map. 94 func (t *HashMap[T1, T2]) Get(k T1) (v T2, ok bool) { 95 var hash uint64 = k.Hash() 96 return t.buckets[indexOf(hash, t.bucketLen)].retrieve(k) 97 } 98 99 // Deletes a value from the map. 100 func (t *HashMap[T1, T2]) Delete(k T1) (ok bool) { 101 var hash uint64 = k.Hash() 102 ok = t.buckets[indexOf(hash, t.bucketLen)].delete(k) 103 if ok { 104 t.len-- 105 } 106 return 107 } 108 109 // Deletes a value from the map if the predicate returns true. 110 func (t *HashMap[T1, T2]) DeleteIf(p func(T1, T2) bool) (amountDeleted int) { 111 var deleted int 112 113 for _, bucket := range t.buckets { 114 deleted = bucket.deleteIf(p) 115 amountDeleted += deleted 116 } 117 118 t.len -= amountDeleted 119 return 120 } 121 122 // Returns the number of items in the map. 123 func (t *HashMap[T1, T2]) Len() int { 124 return t.len 125 } 126 127 // Returns all of the keys in the map. 128 func (t *HashMap[T1, T2]) Keys() []T1 { 129 var keys = make([]T1, t.len, t.len) 130 var i int 131 for _, bucket := range t.buckets { 132 bucket.traverse(func(k T1, v T2) bool { 133 keys[i] = k 134 i++ 135 return true 136 }) 137 } 138 139 return keys 140 } 141 142 // Returns all of the values in the map. 143 func (t *HashMap[T1, T2]) Values() []T2 { 144 var values = make([]T2, t.len, t.len) 145 var i int 146 for _, bucket := range t.buckets { 147 bucket.traverse(func(k T1, v T2) bool { 148 values[i] = v 149 i++ 150 return true 151 }) 152 } 153 return values 154 } 155 156 // Clear the map. 157 func (t *HashMap[T1, T2]) Clear() { 158 for i := range t.buckets { 159 t.buckets[i] = &bucket[T1, T2]{} 160 } 161 t.len = 0 162 } 163 164 // Range over the map. 165 func (t *HashMap[T1, T2]) Range(f func(k T1, v T2) (continueLoop bool)) { 166 for _, bucket := range t.buckets { 167 if !bucket.traverse(f) { 168 return 169 } 170 } 171 } 172 173 // Pop a value from the map. 174 // 175 // Returns the value and a boolean indicating whether the value was found. 176 func (t *HashMap[T1, T2]) Pop(k T1) (v T2, ok bool) { 177 var hash uint64 = k.Hash() 178 v, ok = t.buckets[indexOf(hash, t.bucketLen)].pop(k) 179 if ok { 180 t.len-- 181 } 182 return 183 } 184 185 // Returns a string representation of the map. 186 func (t *HashMap[T1, T2]) String() string { 187 var b strings.Builder 188 b.WriteString("{") 189 var i int 190 t.Range(func(k T1, v T2) (continueLoop bool) { 191 b.WriteString(fmt.Sprintf("%v:%v\n", k, v)) 192 if i < t.len-1 { 193 b.WriteString(", ") 194 } 195 i++ 196 return true 197 }) 198 b.WriteString("}") 199 return b.String() 200 } 201 202 // Returns the GoString representation of the map. 203 // 204 // Because we allow you to define your own hashing, and comparison functions 205 // we allow you to see every bit of the insides of the map for debugging purposes. 206 func (t *HashMap[T1, T2]) GoString() string { 207 var b strings.Builder 208 b.WriteString("Map[T1, T2]{") 209 for j, bucket := range t.buckets { 210 if bucket._len == 0 { 211 b.WriteString(fmt.Sprintf("\n\tBucket{index: %d, bucketLen: %d, items: []}", j, bucket._len)) 212 if j < len(t.buckets)-1 { 213 b.WriteString(", ") 214 } 215 } else { 216 b.WriteString("\n\tBucket{") 217 b.WriteString(fmt.Sprintf("\n\t\tindex: %d", j)) 218 b.WriteString("\n\t\tbucketLen: ") 219 b.WriteString(strconv.FormatInt(int64(bucket._len), 10)) 220 b.WriteString("\n\t\titems: [") 221 var o int 222 traverseTree(bucket.root, func(n *bucketNode[T1, T2]) bool { 223 b.WriteString("\n\t\t\tbucketNode: {") 224 b.WriteString(fmt.Sprintf("\n\t\t\thash: %d", n._hash)) 225 b.WriteString("\n\t\t\t\tnodes: [") 226 for next := n.next; next != nil; next = next.next { 227 b.WriteString(fmt.Sprintf("%v:%v", next.key, next.value)) 228 if next.next != nil { 229 b.WriteString(" > ") 230 } 231 } 232 b.WriteString("]\n\t\t\t}") 233 return true 234 }) 235 if o > 0 { 236 b.WriteString("\n\t\t") 237 } 238 b.WriteString("]\n\t}") 239 if j < len(t.buckets)-1 { 240 b.WriteString(", ") 241 } 242 } 243 } 244 b.WriteString("\n}") 245 return b.String() 246 }