You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gods/maps/treebidimap/treebidimap.go

123 lines
3.7 KiB
Go

// Copyright (c) 2015, Emir Pasic. All rights reserved.
// 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 two red-black tree.
//
// This structure guarantees that the map will be in both ascending key and value order.
//
// 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.
//
// Structure is not thread safe.
//
// Reference: https://en.wikipedia.org/wiki/Bidirectional_map
package treebidimap
import (
"cmp"
"fmt"
"strings"
"github.com/emirpasic/gods/v2/maps"
"github.com/emirpasic/gods/v2/trees/redblacktree"
"github.com/emirpasic/gods/v2/utils"
)
// Assert Map implementation
var _ maps.BidiMap[string, int] = (*Map[string, int])(nil)
// Map holds the elements in two red-black trees.
type Map[K, V comparable] struct {
forwardMap redblacktree.Tree[K, V]
inverseMap redblacktree.Tree[V, K]
}
// New instantiates a bidirectional map.
func New[K, V cmp.Ordered]() *Map[K, V] {
return &Map[K, V]{
forwardMap: *redblacktree.New[K, V](),
inverseMap: *redblacktree.New[V, K](),
}
}
// NewWith instantiates a bidirectional map.
func NewWith[K, V comparable](keyComparator utils.Comparator[K], valueComparator utils.Comparator[V]) *Map[K, V] {
return &Map[K, V]{
forwardMap: *redblacktree.NewWith[K, V](keyComparator),
inverseMap: *redblacktree.NewWith[V, K](valueComparator),
}
}
// Put inserts element into the map.
func (m *Map[K, V]) Put(key K, value V) {
if v, ok := m.forwardMap.Get(key); ok {
m.inverseMap.Remove(v)
}
if k, ok := m.inverseMap.Get(value); ok {
m.forwardMap.Remove(k)
}
m.forwardMap.Put(key, value)
m.inverseMap.Put(value, key)
}
// 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[K, V]) Get(key K) (value V, found bool) {
return m.forwardMap.Get(key)
}
// 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[K, V]) GetKey(value V) (key K, found bool) {
return m.inverseMap.Get(value)
}
// Remove removes the element from the map by key.
func (m *Map[K, V]) Remove(key K) {
if v, found := m.forwardMap.Get(key); found {
m.forwardMap.Remove(key)
m.inverseMap.Remove(v)
}
}
// Empty returns true if map does not contain any elements
func (m *Map[K, V]) Empty() bool {
return m.Size() == 0
}
// Size returns number of elements in the map.
func (m *Map[K, V]) Size() int {
return m.forwardMap.Size()
}
// Keys returns all keys (ordered).
func (m *Map[K, V]) Keys() []K {
return m.forwardMap.Keys()
}
// Values returns all values (ordered).
func (m *Map[K, V]) Values() []V {
return m.inverseMap.Keys()
}
// Clear removes all elements from the map.
func (m *Map[K, V]) Clear() {
m.forwardMap.Clear()
m.inverseMap.Clear()
}
// String returns a string representation of container
func (m *Map[K, V]) String() string {
str := "TreeBidiMap\nmap["
it := m.Iterator()
for it.Next() {
str += fmt.Sprintf("%v:%v ", it.Key(), it.Value())
}
return strings.TrimRight(str, " ") + "]"
}