From 613722286afd653f60d6bdd7ae025095590305cb Mon Sep 17 00:00:00 2001 From: manuelrojas19 Date: Tue, 9 Jul 2024 22:59:15 -0600 Subject: [PATCH 1/2] Improved HashMap implementation --- structure/hashmap/hashmap.go | 117 +++++++++++++++-------------------- 1 file changed, 49 insertions(+), 68 deletions(-) diff --git a/structure/hashmap/hashmap.go b/structure/hashmap/hashmap.go index b0f5dbb1d..ef75ed029 100644 --- a/structure/hashmap/hashmap.go +++ b/structure/hashmap/hashmap.go @@ -13,14 +13,14 @@ 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 +// New returns a new HashMap instance func New() *HashMap { return &HashMap{ capacity: defaultCapacity, @@ -28,104 +28,85 @@ func New() *HashMap { } } -// Make creates a new HashMap instance with input size and capacity -func Make(size, capacity uint64) HashMap { - return HashMap{ +// Make creates a new HashMap instance with the specified size and capacity +func Make(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)) } From 195428a81c5afc614034f1a02ff9b7fcffd2a9e0 Mon Sep 17 00:00:00 2001 From: manuelrojas19 Date: Wed, 17 Jul 2024 16:22:26 -0600 Subject: [PATCH 2/2] Renamed Make to New --- structure/hashmap/hashmap.go | 8 ++++---- structure/hashmap/hashmap_test.go | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/structure/hashmap/hashmap.go b/structure/hashmap/hashmap.go index ef75ed029..591ac8685 100644 --- a/structure/hashmap/hashmap.go +++ b/structure/hashmap/hashmap.go @@ -20,16 +20,16 @@ type HashMap struct { table []*node } -// New returns a 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 the specified size and capacity -func Make(size, capacity uint64) *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, diff --git a/structure/hashmap/hashmap_test.go b/structure/hashmap/hashmap_test.go index 1699f8c25..7b55df0bb 100644 --- a/structure/hashmap/hashmap_test.go +++ b/structure/hashmap/hashmap_test.go @@ -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) @@ -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)