From 6a675054718ee66593742dcd253c2688ccabdaca Mon Sep 17 00:00:00 2001 From: Dusan Kasan Date: Mon, 1 May 2017 14:37:16 +0200 Subject: [PATCH] CI badges added, gofmt ran --- README.md | 4 ++- hashmap.go | 89 +++++++++++++++++++++++++------------------------ hashmap_test.go | 24 ++++++------- 3 files changed, 60 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index e8cd424..5ca75d9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Hashmap +[![Build Status](https://circleci.com/gh/DusanKasan/hashmap.svg?style=shield&circle-token=:circle-token)](https://circleci.com/gh/DusanKasan/hashmap) [![Coverage Status](https://coveralls.io/repos/github/DusanKasan/hashmap/badge.svg?branch=master)](https://coveralls.io/github/DusanKasan/hashmap?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/DusanKasan/hashmap)](https://goreportcard.com/report/github.com/DusanKasan/hashmap) + A [Red-Black Tree](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) [Hash Map](https://en.wikipedia.org/wiki/Hash_table) implementation in Golang that uses user-supplied hashing algorithms. ## Usage @@ -60,7 +62,7 @@ As this hash map supports keys and values of any type (by type hinting interface Direct support for code generation by this package is still considered but not yet implemented. -##TODO +## TODO - implement as thread safe - threadsafety: don't lock the whole tree but separate nodes? diff --git a/hashmap.go b/hashmap.go index ec9c956..fbd7716 100644 --- a/hashmap.go +++ b/hashmap.go @@ -1,34 +1,37 @@ package hashmap type color bool + const black color = false const red color = true type matchPosition int8 + const greater matchPosition = 1 const same matchPosition = 0 const lower matchPosition = -1 type rbTreeNode struct { - color color + color color - keyHash int64 - key interface{} - value interface{} + keyHash int64 + key interface{} + value interface{} - parent *rbTreeNode + parent *rbTreeNode - left *rbTreeNode - right *rbTreeNode + left *rbTreeNode + right *rbTreeNode collisions map[interface{}]interface{} } type rbTree struct { - root *rbTreeNode - hashFunc func(interface{}) int64 + root *rbTreeNode + hashFunc func(interface{}) int64 } +// Create new hash map with supplied hashing function func New(hashFunc func(i interface{}) int64) *rbTree { return &rbTree{hashFunc: hashFunc} } @@ -38,11 +41,11 @@ func (rb *rbTree) Insert(key, value interface{}) { child := &rbTreeNode{ keyHash: keyHash, - key: key, - value: value, - left: &rbTreeNode{}, - right: &rbTreeNode{}, - color: red, + key: key, + value: value, + left: &rbTreeNode{}, + right: &rbTreeNode{}, + color: red, } child.collisions = map[interface{}]interface{}{} @@ -50,7 +53,6 @@ func (rb *rbTree) Insert(key, value interface{}) { //find insertion parent and position where we should place child parent, position := findInsertionParent(rb.root, keyHash) - //insert the child node switch position { case greater: @@ -79,7 +81,7 @@ func (rb *rbTree) Insert(key, value interface{}) { for { if child.parent == nil { rb.root = child - break; + break } child = child.parent @@ -171,7 +173,7 @@ func (rb *rbTree) Remove(key interface{}) (found bool) { if len(node.collisions) > 0 { if key == node.key { - for k, v := range (node.collisions) { + for k, v := range node.collisions { node.key = k node.value = v break @@ -226,7 +228,7 @@ func (rb *rbTree) Remove(key interface{}) (found bool) { rb.root = nil } - break; + break } replacementNodeChild = replacementNodeChild.parent @@ -236,7 +238,7 @@ func (rb *rbTree) Remove(key interface{}) (found bool) { } func isLeaf(node *rbTreeNode) bool { - return node.left == nil && node.right == nil && node.color == black; + return node.left == nil && node.right == nil && node.color == black } //if node is the new root, finish @@ -264,24 +266,23 @@ func deleteCase2(node *rbTreeNode) { deleteCase3(node) } - func deleteCase3(node *rbTreeNode) { sibling := getSibling(node) if node.parent.color == black && sibling.color == black && sibling.left.color == black && sibling.right.color == black { - sibling.color = red; - deleteCase1(node.parent); + sibling.color = red + deleteCase1(node.parent) } else { - deleteCase4(node); + deleteCase4(node) } } func deleteCase4(node *rbTreeNode) { sibling := getSibling(node) if node.parent.color == red && sibling.color == black && sibling.left.color == black && sibling.right.color == black { - sibling.color = red; - node.parent.color = black; + sibling.color = red + node.parent.color = black } else { - deleteCase5(node); + deleteCase5(node) } } @@ -305,15 +306,15 @@ func deleteCase5(node *rbTreeNode) { func deleteCase6(node *rbTreeNode) { sibling := getSibling(node) - sibling.color = node.parent.color; - node.parent.color = black; + sibling.color = node.parent.color + node.parent.color = black - if (node == node.parent.left) { - sibling.right.color = black; - rotateLeft(node.parent); + if node == node.parent.left { + sibling.right.color = black + rotateLeft(node.parent) } else { - sibling.left.color = black; - rotateRight(node.parent); + sibling.left.color = black + rotateRight(node.parent) } } @@ -411,18 +412,19 @@ func findInsertionParent(n *rbTreeNode, keyHash int64) (*rbTreeNode, matchPositi if keyHash > n.keyHash { if isLeaf(n.right) { return n, greater - } else { - return findInsertionParent(n.right, keyHash) } + + return findInsertionParent(n.right, keyHash) + } else if keyHash < n.keyHash { if isLeaf(n.left) { return n, lower - } else { - return findInsertionParent(n.left, keyHash) } - } else { - return n, same + + return findInsertionParent(n.left, keyHash) } + + return n, same } func getGrandparent(n *rbTreeNode) (g *rbTreeNode) { @@ -439,9 +441,9 @@ func getUncle(n *rbTreeNode) (u *rbTreeNode) { return } else if n.parent == g.left { return g.right - } else { - return g.left } + + return g.left } func getSibling(n *rbTreeNode) (u *rbTreeNode) { @@ -451,8 +453,7 @@ func getSibling(n *rbTreeNode) (u *rbTreeNode) { if n.parent.left == n { return n.parent.right - } else { - return n.parent.left } -} + return n.parent.left +} diff --git a/hashmap_test.go b/hashmap_test.go index 0e56a14..e572523 100644 --- a/hashmap_test.go +++ b/hashmap_test.go @@ -1,9 +1,9 @@ package hashmap_test import ( - "testing" "github.com/DusanKasan/hashmap" "math/rand" + "testing" "time" ) @@ -13,7 +13,7 @@ func TestHashmap(t *testing.T) { //hash function that causes collisions hashFunc := func(i interface{}) int64 { v := i.(int64) - if v != 0 && v % 5 == 0 { + if v != 0 && v%5 == 0 { return v - 1 } @@ -28,12 +28,12 @@ func TestHashmap(t *testing.T) { m := hashmap.New(hashFunc) - for key, value := range (input) { + for key, value := range input { t.Logf("Inserting key: %v", key) m.Insert(key, value) } - for key, value := range (input) { + for key, value := range input { v, found := m.Get(key) if !found { t.Errorf("Key not found: %v", key) @@ -51,7 +51,7 @@ func TestHashmap(t *testing.T) { removedKeys := []int64{} for len(keys) > 0 { preservedKeys := []int64{} - for i, k := range(keys) { + for i, k := range keys { if i == 0 { t.Logf("Removing key: %v", k) @@ -68,7 +68,7 @@ func TestHashmap(t *testing.T) { } keys = preservedKeys - for _, k := range(keys) { + for _, k := range keys { v, found := m.Get(k) if !found { t.Errorf("Key %v not found!", k) @@ -77,7 +77,7 @@ func TestHashmap(t *testing.T) { } } - for _, k := range(removedKeys) { + for _, k := range removedKeys { _, found := m.Get(k) if found { t.Errorf("Key %v found when it shouldn't have been!", k) @@ -89,12 +89,12 @@ func TestHashmap(t *testing.T) { } //generate a map with randomized keys and values -func generateInputPool(size int) (map[int64]int64) { +func generateInputPool(size int) map[int64]int64 { r := map[int64]int64{} values := rand.Perm(size * 4) - for index, key := range (rand.Perm(size * 4)) { - if index % 4 == 0 { + for index, key := range rand.Perm(size * 4) { + if index%4 == 0 { r[int64(key)] = int64(values[index]) } } @@ -105,13 +105,13 @@ func generateInputPool(size int) (map[int64]int64) { func getShuffledKeys(input map[int64]int64) []int64 { keys := []int64{} - for key, _ := range(input) { + for key, _ := range input { keys = append(keys, key) } order := rand.Perm(len(keys)) result := []int64{} - for _, k := range(order) { + for _, k := range order { result = append(result, keys[k]) }