CI badges added, gofmt ran

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

@ -1,5 +1,7 @@
# Hashmap # 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. 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 ## 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. Direct support for code generation by this package is still considered but not yet implemented.
##TODO ## TODO
- implement as thread safe - implement as thread safe
- threadsafety: don't lock the whole tree but separate nodes? - threadsafety: don't lock the whole tree but separate nodes?

@ -1,34 +1,37 @@
package hashmap package hashmap
type color bool type color bool
const black color = false const black color = false
const red color = true const red color = true
type matchPosition int8 type matchPosition int8
const greater matchPosition = 1 const greater matchPosition = 1
const same matchPosition = 0 const same matchPosition = 0
const lower matchPosition = -1 const lower matchPosition = -1
type rbTreeNode struct { type rbTreeNode struct {
color color color color
keyHash int64 keyHash int64
key interface{} key interface{}
value interface{} value interface{}
parent *rbTreeNode parent *rbTreeNode
left *rbTreeNode left *rbTreeNode
right *rbTreeNode right *rbTreeNode
collisions map[interface{}]interface{} collisions map[interface{}]interface{}
} }
type rbTree struct { type rbTree struct {
root *rbTreeNode root *rbTreeNode
hashFunc func(interface{}) int64 hashFunc func(interface{}) int64
} }
// Create new hash map with supplied hashing function
func New(hashFunc func(i interface{}) int64) *rbTree { func New(hashFunc func(i interface{}) int64) *rbTree {
return &rbTree{hashFunc: hashFunc} return &rbTree{hashFunc: hashFunc}
} }
@ -38,11 +41,11 @@ func (rb *rbTree) Insert(key, value interface{}) {
child := &rbTreeNode{ child := &rbTreeNode{
keyHash: keyHash, keyHash: keyHash,
key: key, key: key,
value: value, value: value,
left: &rbTreeNode{}, left: &rbTreeNode{},
right: &rbTreeNode{}, right: &rbTreeNode{},
color: red, color: red,
} }
child.collisions = map[interface{}]interface{}{} 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 //find insertion parent and position where we should place child
parent, position := findInsertionParent(rb.root, keyHash) parent, position := findInsertionParent(rb.root, keyHash)
//insert the child node //insert the child node
switch position { switch position {
case greater: case greater:
@ -79,7 +81,7 @@ func (rb *rbTree) Insert(key, value interface{}) {
for { for {
if child.parent == nil { if child.parent == nil {
rb.root = child rb.root = child
break; break
} }
child = child.parent child = child.parent
@ -171,7 +173,7 @@ func (rb *rbTree) Remove(key interface{}) (found bool) {
if len(node.collisions) > 0 { if len(node.collisions) > 0 {
if key == node.key { if key == node.key {
for k, v := range (node.collisions) { for k, v := range node.collisions {
node.key = k node.key = k
node.value = v node.value = v
break break
@ -226,7 +228,7 @@ func (rb *rbTree) Remove(key interface{}) (found bool) {
rb.root = nil rb.root = nil
} }
break; break
} }
replacementNodeChild = replacementNodeChild.parent replacementNodeChild = replacementNodeChild.parent
@ -236,7 +238,7 @@ func (rb *rbTree) Remove(key interface{}) (found bool) {
} }
func isLeaf(node *rbTreeNode) 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 //if node is the new root, finish
@ -264,24 +266,23 @@ func deleteCase2(node *rbTreeNode) {
deleteCase3(node) deleteCase3(node)
} }
func deleteCase3(node *rbTreeNode) { func deleteCase3(node *rbTreeNode) {
sibling := getSibling(node) sibling := getSibling(node)
if node.parent.color == black && sibling.color == black && sibling.left.color == black && sibling.right.color == black { if node.parent.color == black && sibling.color == black && sibling.left.color == black && sibling.right.color == black {
sibling.color = red; sibling.color = red
deleteCase1(node.parent); deleteCase1(node.parent)
} else { } else {
deleteCase4(node); deleteCase4(node)
} }
} }
func deleteCase4(node *rbTreeNode) { func deleteCase4(node *rbTreeNode) {
sibling := getSibling(node) sibling := getSibling(node)
if node.parent.color == red && sibling.color == black && sibling.left.color == black && sibling.right.color == black { if node.parent.color == red && sibling.color == black && sibling.left.color == black && sibling.right.color == black {
sibling.color = red; sibling.color = red
node.parent.color = black; node.parent.color = black
} else { } else {
deleteCase5(node); deleteCase5(node)
} }
} }
@ -305,15 +306,15 @@ func deleteCase5(node *rbTreeNode) {
func deleteCase6(node *rbTreeNode) { func deleteCase6(node *rbTreeNode) {
sibling := getSibling(node) sibling := getSibling(node)
sibling.color = node.parent.color; sibling.color = node.parent.color
node.parent.color = black; node.parent.color = black
if (node == node.parent.left) { if node == node.parent.left {
sibling.right.color = black; sibling.right.color = black
rotateLeft(node.parent); rotateLeft(node.parent)
} else { } else {
sibling.left.color = black; sibling.left.color = black
rotateRight(node.parent); rotateRight(node.parent)
} }
} }
@ -411,18 +412,19 @@ func findInsertionParent(n *rbTreeNode, keyHash int64) (*rbTreeNode, matchPositi
if keyHash > n.keyHash { if keyHash > n.keyHash {
if isLeaf(n.right) { if isLeaf(n.right) {
return n, greater return n, greater
} else {
return findInsertionParent(n.right, keyHash)
} }
return findInsertionParent(n.right, keyHash)
} else if keyHash < n.keyHash { } else if keyHash < n.keyHash {
if isLeaf(n.left) { if isLeaf(n.left) {
return n, lower 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) { func getGrandparent(n *rbTreeNode) (g *rbTreeNode) {
@ -439,9 +441,9 @@ func getUncle(n *rbTreeNode) (u *rbTreeNode) {
return return
} else if n.parent == g.left { } else if n.parent == g.left {
return g.right return g.right
} else {
return g.left
} }
return g.left
} }
func getSibling(n *rbTreeNode) (u *rbTreeNode) { func getSibling(n *rbTreeNode) (u *rbTreeNode) {
@ -451,8 +453,7 @@ func getSibling(n *rbTreeNode) (u *rbTreeNode) {
if n.parent.left == n { if n.parent.left == n {
return n.parent.right return n.parent.right
} else {
return n.parent.left
} }
}
return n.parent.left
}

@ -1,9 +1,9 @@
package hashmap_test package hashmap_test
import ( import (
"testing"
"github.com/DusanKasan/hashmap" "github.com/DusanKasan/hashmap"
"math/rand" "math/rand"
"testing"
"time" "time"
) )
@ -13,7 +13,7 @@ func TestHashmap(t *testing.T) {
//hash function that causes collisions //hash function that causes collisions
hashFunc := func(i interface{}) int64 { hashFunc := func(i interface{}) int64 {
v := i.(int64) v := i.(int64)
if v != 0 && v % 5 == 0 { if v != 0 && v%5 == 0 {
return v - 1 return v - 1
} }
@ -28,12 +28,12 @@ func TestHashmap(t *testing.T) {
m := hashmap.New(hashFunc) m := hashmap.New(hashFunc)
for key, value := range (input) { for key, value := range input {
t.Logf("Inserting key: %v", key) t.Logf("Inserting key: %v", key)
m.Insert(key, value) m.Insert(key, value)
} }
for key, value := range (input) { for key, value := range input {
v, found := m.Get(key) v, found := m.Get(key)
if !found { if !found {
t.Errorf("Key not found: %v", key) t.Errorf("Key not found: %v", key)
@ -51,7 +51,7 @@ func TestHashmap(t *testing.T) {
removedKeys := []int64{} removedKeys := []int64{}
for len(keys) > 0 { for len(keys) > 0 {
preservedKeys := []int64{} preservedKeys := []int64{}
for i, k := range(keys) { for i, k := range keys {
if i == 0 { if i == 0 {
t.Logf("Removing key: %v", k) t.Logf("Removing key: %v", k)
@ -68,7 +68,7 @@ func TestHashmap(t *testing.T) {
} }
keys = preservedKeys keys = preservedKeys
for _, k := range(keys) { for _, k := range keys {
v, found := m.Get(k) v, found := m.Get(k)
if !found { if !found {
t.Errorf("Key %v not found!", k) 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) _, found := m.Get(k)
if found { if found {
t.Errorf("Key %v found when it shouldn't have been!", k) 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 //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{} r := map[int64]int64{}
values := rand.Perm(size * 4) values := rand.Perm(size * 4)
for index, key := range (rand.Perm(size * 4)) { for index, key := range rand.Perm(size * 4) {
if index % 4 == 0 { if index%4 == 0 {
r[int64(key)] = int64(values[index]) r[int64(key)] = int64(values[index])
} }
} }
@ -105,13 +105,13 @@ func generateInputPool(size int) (map[int64]int64) {
func getShuffledKeys(input map[int64]int64) []int64 { func getShuffledKeys(input map[int64]int64) []int64 {
keys := []int64{} keys := []int64{}
for key, _ := range(input) { for key, _ := range input {
keys = append(keys, key) keys = append(keys, key)
} }
order := rand.Perm(len(keys)) order := rand.Perm(len(keys))
result := []int64{} result := []int64{}
for _, k := range(order) { for _, k := range order {
result = append(result, keys[k]) result = append(result, keys[k])
} }

Loading…
Cancel
Save