- TreeBidiMap core implemented (with tests)

pull/25/head
Emir Pasic 8 years ago
parent 20e2a00f13
commit 0d9a0e6124

@ -96,7 +96,7 @@ func (m *Map) Clear() {
// String returns a string representation of container // String returns a string representation of container
func (m *Map) String() string { func (m *Map) String() string {
str := "HashMap\n" str := "HashBidiMap\n"
str += fmt.Sprintf("%v", m.forwardMap) str += fmt.Sprintf("%v", m.forwardMap)
return str return str
} }

@ -2,16 +2,16 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package treebidimap implements a bidirectional map backed by a red-black tree. // Package treebidimap implements a bidirectional map backed by two red-black tree.
//
// This structure guarantees that the map will be in both ascending key and value order. // This structure guarantees that the map will be in both ascending key and value order.
// The goal with this structure is to avoid duplication of elements, which can be significant if contained elements are large. //
// Other than key and value ordering, the goal with this structure is to avoid duplication of elements, which can be significant if contained elements are large.
// //
// A bidirectional map, or hash bag, is an associative data structure in which the (key,value) pairs form a one-to-one correspondence. // A bidirectional map, or hash bag, is an associative data structure in which the (key,value) pairs form a one-to-one correspondence.
// Thus the binary relation is functional in each direction: value can also act as a key to key. // Thus the binary relation is functional in each direction: value can also act as a key to key.
// A pair (a,b) thus provides a unique coupling between 'a' and 'b' so that 'b' can be found when 'a' is used as a key and 'a' can be found when 'b' is used as a key. // A pair (a,b) thus provides a unique coupling between 'a' and 'b' so that 'b' can be found when 'a' is used as a key and 'a' can be found when 'b' is used as a key.
// //
// Elements are unordered in the map.
//
// Structure is not thread safe. // Structure is not thread safe.
// //
// Reference: https://en.wikipedia.org/wiki/Bidirectional_map // Reference: https://en.wikipedia.org/wiki/Bidirectional_map
@ -20,53 +20,79 @@ package treebidimap
import ( import (
"fmt" "fmt"
"github.com/emirpasic/gods/maps" "github.com/emirpasic/gods/maps"
"github.com/emirpasic/gods/maps/hashmap" "github.com/emirpasic/gods/trees/redblacktree"
"github.com/emirpasic/gods/utils"
) )
func assertMapImplementation() { func assertMapImplementation() {
var _ maps.BidiMap = (*Map)(nil) var _ maps.BidiMap = (*Map)(nil)
} }
// Map holds the elements in two hashmaps. // Map holds the elements in two red-black trees.
type Map struct { type Map struct {
forwardMap hashmap.Map forwardMap redblacktree.Tree
inverseMap hashmap.Map inverseMap redblacktree.Tree
}
type data struct {
key interface{}
value interface{}
}
// NewWith instantiates a bidirectional map.
func NewWith(keyComparator utils.Comparator, valueComparator utils.Comparator) *Map {
return &Map{
forwardMap: *redblacktree.NewWith(keyComparator),
inverseMap: *redblacktree.NewWith(valueComparator),
}
}
// NewWithIntComparators instantiates a bidirectional map with the IntComparator for key and value, i.e. keys and values are of type int.
func NewWithIntComparators() *Map {
return NewWith(utils.IntComparator, utils.IntComparator)
} }
// New instantiates a bidirectional map. // NewWithStringComparators instantiates a bidirectional map with the StringComparator for key and value, i.e. keys and values are of type string.
func New() *Map { func NewWithStringComparators() *Map {
return &Map{*hashmap.New(), *hashmap.New()} return NewWith(utils.StringComparator, utils.StringComparator)
} }
// Put inserts element into the map. // Put inserts element into the map.
func (m *Map) Put(key interface{}, value interface{}) { func (m *Map) Put(key interface{}, value interface{}) {
if valueByKey, ok := m.forwardMap.Get(key); ok { if d, ok := m.forwardMap.Get(key); ok {
m.inverseMap.Remove(valueByKey) m.inverseMap.Remove(d.(*data).value)
} }
if keyByValue, ok := m.inverseMap.Get(value); ok { if d, ok := m.inverseMap.Get(value); ok {
m.forwardMap.Remove(keyByValue) m.forwardMap.Remove(d.(*data).key)
} }
m.forwardMap.Put(key, value) d := &data{key: key, value: value}
m.inverseMap.Put(value, key) m.forwardMap.Put(key, d)
m.inverseMap.Put(value, d)
} }
// Get searches the element in the map by key and returns its value or nil if key is not found in map. // Get searches the element in the map by key and returns its value or nil if key is not found in map.
// Second return parameter is true if key was found, otherwise false. // Second return parameter is true if key was found, otherwise false.
func (m *Map) Get(key interface{}) (value interface{}, found bool) { func (m *Map) Get(key interface{}) (value interface{}, found bool) {
return m.forwardMap.Get(key) if d, ok := m.forwardMap.Get(key); ok {
return d.(*data).value, true
}
return nil, false
} }
// GetKey searches the element in the map by value and returns its key or nil if value is not found in map. // GetKey searches the element in the map by value and returns its key or nil if value is not found in map.
// Second return parameter is true if value was found, otherwise false. // Second return parameter is true if value was found, otherwise false.
func (m *Map) GetKey(value interface{}) (key interface{}, found bool) { func (m *Map) GetKey(value interface{}) (key interface{}, found bool) {
return m.inverseMap.Get(value) if d, ok := m.inverseMap.Get(value); ok {
return d.(*data).key, true
}
return nil, false
} }
// Remove removes the element from the map by key. // Remove removes the element from the map by key.
func (m *Map) Remove(key interface{}) { func (m *Map) Remove(key interface{}) {
if value, found := m.forwardMap.Get(key); found { if d, found := m.forwardMap.Get(key); found {
m.forwardMap.Remove(key) m.forwardMap.Remove(key)
m.inverseMap.Remove(value) m.inverseMap.Remove(d.(*data).value)
} }
} }
@ -80,12 +106,12 @@ func (m *Map) Size() int {
return m.forwardMap.Size() return m.forwardMap.Size()
} }
// Keys returns all keys (random order). // Keys returns all keys (ordered).
func (m *Map) Keys() []interface{} { func (m *Map) Keys() []interface{} {
return m.forwardMap.Keys() return m.forwardMap.Keys()
} }
// Values returns all values (random order). // Values returns all values (ordered).
func (m *Map) Values() []interface{} { func (m *Map) Values() []interface{} {
return m.inverseMap.Keys() return m.inverseMap.Keys()
} }
@ -98,7 +124,7 @@ func (m *Map) Clear() {
// String returns a string representation of container // String returns a string representation of container
func (m *Map) String() string { func (m *Map) String() string {
str := "HashMap\n" str := "TreeBidiMap\n"
str += fmt.Sprintf("%v", m.forwardMap) str += fmt.Sprintf("%v", m.forwardMap)
return str return str
} }

@ -6,11 +6,12 @@ package treebidimap
import ( import (
"fmt" "fmt"
"github.com/emirpasic/gods/utils"
"testing" "testing"
) )
func TestMapPut(t *testing.T) { func TestMapPut(t *testing.T) {
m := New() m := NewWith(utils.IntComparator, utils.StringComparator)
m.Put(5, "e") m.Put(5, "e")
m.Put(6, "f") m.Put(6, "f")
m.Put(7, "g") m.Put(7, "g")
@ -52,7 +53,7 @@ func TestMapPut(t *testing.T) {
} }
func TestMapRemove(t *testing.T) { func TestMapRemove(t *testing.T) {
m := New() m := NewWith(utils.IntComparator, utils.StringComparator)
m.Put(5, "e") m.Put(5, "e")
m.Put(6, "f") m.Put(6, "f")
m.Put(7, "g") m.Put(7, "g")
@ -119,7 +120,7 @@ func TestMapRemove(t *testing.T) {
} }
func TestMapGetKey(t *testing.T) { func TestMapGetKey(t *testing.T) {
m := New() m := NewWith(utils.IntComparator, utils.StringComparator)
m.Put(5, "e") m.Put(5, "e")
m.Put(6, "f") m.Put(6, "f")
m.Put(7, "g") m.Put(7, "g")
@ -171,7 +172,7 @@ func sameElements(a []interface{}, b []interface{}) bool {
func BenchmarkMap(b *testing.B) { func BenchmarkMap(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
m := New() m := NewWithIntComparators()
for n := 0; n < 1000; n++ { for n := 0; n < 1000; n++ {
m.Put(n, n) m.Put(n, n)
} }
Loading…
Cancel
Save