Skip to content

Commit

Permalink
Improved HashMap implementation (#729)
Browse files Browse the repository at this point in the history
* Improved HashMap implementation

* Renamed Make to New
  • Loading branch information
manuelrojas19 authored Jul 18, 2024
1 parent 483431b commit ee6fef2
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 72 deletions.
119 changes: 50 additions & 69 deletions structure/hashmap/hashmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,119 +13,100 @@ type node struct {
next *node
}

// HashMap is golang implementation of hashmap
// HashMap is a Golang implementation of a hashmap
type HashMap struct {
capacity uint64
size uint64
table []*node
}

// New return new HashMap instance
func New() *HashMap {
// DefaultNew returns a new HashMap instance with default values
func DefaultNew() *HashMap {
return &HashMap{
capacity: defaultCapacity,
table: make([]*node, defaultCapacity),
}
}

// Make creates a new HashMap instance with input size and capacity
func Make(size, capacity uint64) HashMap {
return HashMap{
// New creates a new HashMap instance with the specified size and capacity
func New(size, capacity uint64) *HashMap {
return &HashMap{
size: size,
capacity: capacity,
table: make([]*node, capacity),
}
}

// Get returns value associated with given key
// Get returns the value associated with the given key
func (hm *HashMap) Get(key any) any {
node := hm.getNodeByHash(hm.hash(key))

node := hm.getNodeByKey(key)
if node != nil {
return node.value
}

return nil
}

// Put puts new key value in hashmap
func (hm *HashMap) Put(key any, value any) any {
return hm.putValue(hm.hash(key), key, value)
}

// Contains checks if given key is stored in hashmap
func (hm *HashMap) Contains(key any) bool {
node := hm.getNodeByHash(hm.hash(key))
return node != nil
}

func (hm *HashMap) putValue(hash uint64, key any, value any) any {
if hm.capacity == 0 {
hm.capacity = defaultCapacity
hm.table = make([]*node, defaultCapacity)
}

node := hm.getNodeByHash(hash)

if node == nil {
hm.table[hash] = newNode(key, value)

} else if node.key == key {
hm.table[hash] = newNodeWithNext(key, value, node)
return value

// Put inserts a new key-value pair into the hashmap
func (hm *HashMap) Put(key, value any) {
index := hm.hash(key)
if hm.table[index] == nil {
hm.table[index] = &node{key: key, value: value}
} else {
hm.resize()
return hm.putValue(hash, key, value)
current := hm.table[index]
for {
if current.key == key {
current.value = value
return
}
if current.next == nil {
break
}
current = current.next
}
current.next = &node{key: key, value: value}
}

hm.size++
if float64(hm.size)/float64(hm.capacity) > 0.75 {
hm.resize()
}
}

return value

// Contains checks if the given key is stored in the hashmap
func (hm *HashMap) Contains(key any) bool {
return hm.getNodeByKey(key) != nil
}

func (hm *HashMap) getNodeByHash(hash uint64) *node {
return hm.table[hash]
// getNodeByKey finds the node associated with the given key
func (hm *HashMap) getNodeByKey(key any) *node {
index := hm.hash(key)
current := hm.table[index]
for current != nil {
if current.key == key {
return current
}
current = current.next
}
return nil
}

// resize doubles the capacity of the hashmap and rehashes all existing entries
func (hm *HashMap) resize() {
oldTable := hm.table
hm.capacity <<= 1

tempTable := hm.table

hm.table = make([]*node, hm.capacity)
hm.size = 0

for i := 0; i < len(tempTable); i++ {
node := tempTable[i]
if node == nil {
continue
for _, head := range oldTable {
for current := head; current != nil; current = current.next {
hm.Put(current.key, current.value)
}

hm.table[hm.hash(node.key)] = node
}
}

func newNode(key any, value any) *node {
return &node{
key: key,
value: value,
}
}

func newNodeWithNext(key any, value any, next *node) *node {
return &node{
key: key,
value: value,
next: next,
}
}

// hash generates a hash value for the given key
func (hm *HashMap) hash(key any) uint64 {
h := fnv.New64a()
_, _ = h.Write([]byte(fmt.Sprintf("%v", key)))

hashValue := h.Sum64()

return (hm.capacity - 1) & (hashValue ^ (hashValue >> 16))
}
5 changes: 2 additions & 3 deletions structure/hashmap/hashmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import (
)

func TestHashMap(t *testing.T) {

mp := hashmap.New()
mp := hashmap.DefaultNew()

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

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

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

0 comments on commit ee6fef2

Please sign in to comment.