From 0d9a0e612443bc32f9c23e78ea6bd1c706eb4683 Mon Sep 17 00:00:00 2001 From: Emir Pasic Date: Sun, 3 Jul 2016 03:18:28 +0200 Subject: [PATCH] - TreeBidiMap core implemented (with tests) --- maps/hashbidimap/hashbidimap.go | 2 +- .../{hashbidimap.go => treebidimap.go} | 74 +++++++++++++------ ...ashbidimap_test.go => treebidimap_test.go} | 9 ++- 3 files changed, 56 insertions(+), 29 deletions(-) rename maps/treebidimap/{hashbidimap.go => treebidimap.go} (56%) rename maps/treebidimap/{hashbidimap_test.go => treebidimap_test.go} (94%) diff --git a/maps/hashbidimap/hashbidimap.go b/maps/hashbidimap/hashbidimap.go index 11027fd..32985ab 100644 --- a/maps/hashbidimap/hashbidimap.go +++ b/maps/hashbidimap/hashbidimap.go @@ -96,7 +96,7 @@ func (m *Map) Clear() { // String returns a string representation of container func (m *Map) String() string { - str := "HashMap\n" + str := "HashBidiMap\n" str += fmt.Sprintf("%v", m.forwardMap) return str } diff --git a/maps/treebidimap/hashbidimap.go b/maps/treebidimap/treebidimap.go similarity index 56% rename from maps/treebidimap/hashbidimap.go rename to maps/treebidimap/treebidimap.go index a48de18..2855542 100644 --- a/maps/treebidimap/hashbidimap.go +++ b/maps/treebidimap/treebidimap.go @@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style // 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. -// 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. // 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. // -// Elements are unordered in the map. -// // Structure is not thread safe. // // Reference: https://en.wikipedia.org/wiki/Bidirectional_map @@ -20,53 +20,79 @@ package treebidimap import ( "fmt" "github.com/emirpasic/gods/maps" - "github.com/emirpasic/gods/maps/hashmap" + "github.com/emirpasic/gods/trees/redblacktree" + "github.com/emirpasic/gods/utils" ) func assertMapImplementation() { var _ maps.BidiMap = (*Map)(nil) } -// Map holds the elements in two hashmaps. +// Map holds the elements in two red-black trees. type Map struct { - forwardMap hashmap.Map - inverseMap hashmap.Map + forwardMap redblacktree.Tree + 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. -func New() *Map { - return &Map{*hashmap.New(), *hashmap.New()} +// NewWithStringComparators instantiates a bidirectional map with the StringComparator for key and value, i.e. keys and values are of type string. +func NewWithStringComparators() *Map { + return NewWith(utils.StringComparator, utils.StringComparator) } // Put inserts element into the map. func (m *Map) Put(key interface{}, value interface{}) { - if valueByKey, ok := m.forwardMap.Get(key); ok { - m.inverseMap.Remove(valueByKey) + if d, ok := m.forwardMap.Get(key); ok { + m.inverseMap.Remove(d.(*data).value) } - if keyByValue, ok := m.inverseMap.Get(value); ok { - m.forwardMap.Remove(keyByValue) + if d, ok := m.inverseMap.Get(value); ok { + m.forwardMap.Remove(d.(*data).key) } - m.forwardMap.Put(key, value) - m.inverseMap.Put(value, key) + d := &data{key: key, value: value} + 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. // Second return parameter is true if key was found, otherwise false. 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. // Second return parameter is true if value was found, otherwise false. 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. 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.inverseMap.Remove(value) + m.inverseMap.Remove(d.(*data).value) } } @@ -80,12 +106,12 @@ func (m *Map) Size() int { return m.forwardMap.Size() } -// Keys returns all keys (random order). +// Keys returns all keys (ordered). func (m *Map) Keys() []interface{} { return m.forwardMap.Keys() } -// Values returns all values (random order). +// Values returns all values (ordered). func (m *Map) Values() []interface{} { return m.inverseMap.Keys() } @@ -98,7 +124,7 @@ func (m *Map) Clear() { // String returns a string representation of container func (m *Map) String() string { - str := "HashMap\n" + str := "TreeBidiMap\n" str += fmt.Sprintf("%v", m.forwardMap) return str } diff --git a/maps/treebidimap/hashbidimap_test.go b/maps/treebidimap/treebidimap_test.go similarity index 94% rename from maps/treebidimap/hashbidimap_test.go rename to maps/treebidimap/treebidimap_test.go index 0731c43..fb95840 100644 --- a/maps/treebidimap/hashbidimap_test.go +++ b/maps/treebidimap/treebidimap_test.go @@ -6,11 +6,12 @@ package treebidimap import ( "fmt" + "github.com/emirpasic/gods/utils" "testing" ) func TestMapPut(t *testing.T) { - m := New() + m := NewWith(utils.IntComparator, utils.StringComparator) m.Put(5, "e") m.Put(6, "f") m.Put(7, "g") @@ -52,7 +53,7 @@ func TestMapPut(t *testing.T) { } func TestMapRemove(t *testing.T) { - m := New() + m := NewWith(utils.IntComparator, utils.StringComparator) m.Put(5, "e") m.Put(6, "f") m.Put(7, "g") @@ -119,7 +120,7 @@ func TestMapRemove(t *testing.T) { } func TestMapGetKey(t *testing.T) { - m := New() + m := NewWith(utils.IntComparator, utils.StringComparator) m.Put(5, "e") m.Put(6, "f") m.Put(7, "g") @@ -171,7 +172,7 @@ func sameElements(a []interface{}, b []interface{}) bool { func BenchmarkMap(b *testing.B) { for i := 0; i < b.N; i++ { - m := New() + m := NewWithIntComparators() for n := 0; n < 1000; n++ { m.Put(n, n) }