CI badges added, gofmt ran

master
Dusan Kasan 7 years ago
parent 2ba748ddc2
commit 6a67505471

@ -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?

@ -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
}

@ -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])
}

Loading…
Cancel
Save