Skip to content

Commit ee6fef2

Browse files
Improved HashMap implementation (#729)
* Improved HashMap implementation * Renamed Make to New
1 parent 483431b commit ee6fef2

File tree

2 files changed

+52
-72
lines changed

2 files changed

+52
-72
lines changed

structure/hashmap/hashmap.go

+50-69
Original file line numberDiff line numberDiff line change
@@ -13,119 +13,100 @@ type node struct {
1313
next *node
1414
}
1515

16-
// HashMap is golang implementation of hashmap
16+
// HashMap is a Golang implementation of a hashmap
1717
type HashMap struct {
1818
capacity uint64
1919
size uint64
2020
table []*node
2121
}
2222

23-
// New return new HashMap instance
24-
func New() *HashMap {
23+
// DefaultNew returns a new HashMap instance with default values
24+
func DefaultNew() *HashMap {
2525
return &HashMap{
2626
capacity: defaultCapacity,
2727
table: make([]*node, defaultCapacity),
2828
}
2929
}
3030

31-
// Make creates a new HashMap instance with input size and capacity
32-
func Make(size, capacity uint64) HashMap {
33-
return HashMap{
31+
// New creates a new HashMap instance with the specified size and capacity
32+
func New(size, capacity uint64) *HashMap {
33+
return &HashMap{
3434
size: size,
3535
capacity: capacity,
3636
table: make([]*node, capacity),
3737
}
3838
}
3939

40-
// Get returns value associated with given key
40+
// Get returns the value associated with the given key
4141
func (hm *HashMap) Get(key any) any {
42-
node := hm.getNodeByHash(hm.hash(key))
43-
42+
node := hm.getNodeByKey(key)
4443
if node != nil {
4544
return node.value
4645
}
47-
4846
return nil
4947
}
5048

51-
// Put puts new key value in hashmap
52-
func (hm *HashMap) Put(key any, value any) any {
53-
return hm.putValue(hm.hash(key), key, value)
54-
}
55-
56-
// Contains checks if given key is stored in hashmap
57-
func (hm *HashMap) Contains(key any) bool {
58-
node := hm.getNodeByHash(hm.hash(key))
59-
return node != nil
60-
}
61-
62-
func (hm *HashMap) putValue(hash uint64, key any, value any) any {
63-
if hm.capacity == 0 {
64-
hm.capacity = defaultCapacity
65-
hm.table = make([]*node, defaultCapacity)
66-
}
67-
68-
node := hm.getNodeByHash(hash)
69-
70-
if node == nil {
71-
hm.table[hash] = newNode(key, value)
72-
73-
} else if node.key == key {
74-
hm.table[hash] = newNodeWithNext(key, value, node)
75-
return value
76-
49+
// Put inserts a new key-value pair into the hashmap
50+
func (hm *HashMap) Put(key, value any) {
51+
index := hm.hash(key)
52+
if hm.table[index] == nil {
53+
hm.table[index] = &node{key: key, value: value}
7754
} else {
78-
hm.resize()
79-
return hm.putValue(hash, key, value)
55+
current := hm.table[index]
56+
for {
57+
if current.key == key {
58+
current.value = value
59+
return
60+
}
61+
if current.next == nil {
62+
break
63+
}
64+
current = current.next
65+
}
66+
current.next = &node{key: key, value: value}
8067
}
81-
8268
hm.size++
69+
if float64(hm.size)/float64(hm.capacity) > 0.75 {
70+
hm.resize()
71+
}
72+
}
8373

84-
return value
85-
74+
// Contains checks if the given key is stored in the hashmap
75+
func (hm *HashMap) Contains(key any) bool {
76+
return hm.getNodeByKey(key) != nil
8677
}
8778

88-
func (hm *HashMap) getNodeByHash(hash uint64) *node {
89-
return hm.table[hash]
79+
// getNodeByKey finds the node associated with the given key
80+
func (hm *HashMap) getNodeByKey(key any) *node {
81+
index := hm.hash(key)
82+
current := hm.table[index]
83+
for current != nil {
84+
if current.key == key {
85+
return current
86+
}
87+
current = current.next
88+
}
89+
return nil
9090
}
9191

92+
// resize doubles the capacity of the hashmap and rehashes all existing entries
9293
func (hm *HashMap) resize() {
94+
oldTable := hm.table
9395
hm.capacity <<= 1
94-
95-
tempTable := hm.table
96-
9796
hm.table = make([]*node, hm.capacity)
97+
hm.size = 0
9898

99-
for i := 0; i < len(tempTable); i++ {
100-
node := tempTable[i]
101-
if node == nil {
102-
continue
99+
for _, head := range oldTable {
100+
for current := head; current != nil; current = current.next {
101+
hm.Put(current.key, current.value)
103102
}
104-
105-
hm.table[hm.hash(node.key)] = node
106-
}
107-
}
108-
109-
func newNode(key any, value any) *node {
110-
return &node{
111-
key: key,
112-
value: value,
113-
}
114-
}
115-
116-
func newNodeWithNext(key any, value any, next *node) *node {
117-
return &node{
118-
key: key,
119-
value: value,
120-
next: next,
121103
}
122104
}
123105

106+
// hash generates a hash value for the given key
124107
func (hm *HashMap) hash(key any) uint64 {
125108
h := fnv.New64a()
126109
_, _ = h.Write([]byte(fmt.Sprintf("%v", key)))
127-
128110
hashValue := h.Sum64()
129-
130111
return (hm.capacity - 1) & (hashValue ^ (hashValue >> 16))
131112
}

structure/hashmap/hashmap_test.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import (
77
)
88

99
func TestHashMap(t *testing.T) {
10-
11-
mp := hashmap.New()
10+
mp := hashmap.DefaultNew()
1211

1312
t.Run("Test 1: Put(10) and checking if Get() is correct", func(t *testing.T) {
1413
mp.Put("test", 10)
@@ -67,7 +66,7 @@ func TestHashMap(t *testing.T) {
6766
})
6867

6968
t.Run("Test 8: Resizing a map", func(t *testing.T) {
70-
mp := hashmap.Make(4, 4)
69+
mp := hashmap.New(4, 4)
7170

7271
for i := 0; i < 20; i++ {
7372
mp.Put(i, 40)

0 commit comments

Comments
 (0)