github.com/mymmsc/gox@v1.3.33/util/treebidimap/treebidimap.go (about)

     1  // Copyright (c) 2015, Emir Pasic. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package treebidimap implements a bidirectional map backed by two red-black tree.
     6  //
     7  // This structure guarantees that the map will be in both ascending key and value order.
     8  //
     9  // 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.
    10  //
    11  // A bidirectional map, or hash bag, is an associative data structure in which the (key,value) pairs form a one-to-one correspondence.
    12  // Thus the binary relation is functional in each direction: value can also act as a key to key.
    13  // 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.
    14  //
    15  // Structure is not thread safe.
    16  //
    17  // Reference: https://en.wikipedia.org/wiki/Bidirectional_map
    18  package treebidimap
    19  
    20  import (
    21  	"fmt"
    22  	"github.com/mymmsc/gox/util"
    23  	"github.com/mymmsc/gox/util/redblacktree"
    24  	"strings"
    25  )
    26  
    27  func assertMapImplementation() {
    28  	var _ util.BidiMap = (*Map)(nil)
    29  }
    30  
    31  // Map holds the elements in two red-black trees.
    32  type Map struct {
    33  	forwardMap      redblacktree.Tree
    34  	inverseMap      redblacktree.Tree
    35  	keyComparator   util.Comparator
    36  	valueComparator util.Comparator
    37  }
    38  
    39  type data struct {
    40  	key   interface{}
    41  	value interface{}
    42  }
    43  
    44  // NewWith instantiates a bidirectional map.
    45  func NewWith(keyComparator util.Comparator, valueComparator util.Comparator) *Map {
    46  	return &Map{
    47  		forwardMap:      *redblacktree.NewWith(keyComparator),
    48  		inverseMap:      *redblacktree.NewWith(valueComparator),
    49  		keyComparator:   keyComparator,
    50  		valueComparator: valueComparator,
    51  	}
    52  }
    53  
    54  // NewWithIntComparators instantiates a bidirectional map with the IntComparator for key and value, i.e. keys and values are of type int.
    55  func NewWithIntComparators() *Map {
    56  	return NewWith(util.IntComparator, util.IntComparator)
    57  }
    58  
    59  // NewWithStringComparators instantiates a bidirectional map with the StringComparator for key and value, i.e. keys and values are of type string.
    60  func NewWithStringComparators() *Map {
    61  	return NewWith(util.StringComparator, util.StringComparator)
    62  }
    63  
    64  // Put inserts element into the map.
    65  func (m *Map) Put(key interface{}, value interface{}) {
    66  	if d, ok := m.forwardMap.Get(key); ok {
    67  		m.inverseMap.Remove(d.(*data).value)
    68  	}
    69  	if d, ok := m.inverseMap.Get(value); ok {
    70  		m.forwardMap.Remove(d.(*data).key)
    71  	}
    72  	d := &data{key: key, value: value}
    73  	m.forwardMap.Put(key, d)
    74  	m.inverseMap.Put(value, d)
    75  }
    76  
    77  // Get searches the element in the map by key and returns its value or nil if key is not found in map.
    78  // Second return parameter is true if key was found, otherwise false.
    79  func (m *Map) Get(key interface{}) (value interface{}, found bool) {
    80  	if d, ok := m.forwardMap.Get(key); ok {
    81  		return d.(*data).value, true
    82  	}
    83  	return nil, false
    84  }
    85  
    86  // GetKey searches the element in the map by value and returns its key or nil if value is not found in map.
    87  // Second return parameter is true if value was found, otherwise false.
    88  func (m *Map) GetKey(value interface{}) (key interface{}, found bool) {
    89  	if d, ok := m.inverseMap.Get(value); ok {
    90  		return d.(*data).key, true
    91  	}
    92  	return nil, false
    93  }
    94  
    95  // Remove removes the element from the map by key.
    96  func (m *Map) Remove(key interface{}) {
    97  	if d, found := m.forwardMap.Get(key); found {
    98  		m.forwardMap.Remove(key)
    99  		m.inverseMap.Remove(d.(*data).value)
   100  	}
   101  }
   102  
   103  // Empty returns true if map does not contain any elements
   104  func (m *Map) Empty() bool {
   105  	return m.Size() == 0
   106  }
   107  
   108  // Size returns number of elements in the map.
   109  func (m *Map) Size() int {
   110  	return m.forwardMap.Size()
   111  }
   112  
   113  // Keys returns all keys (ordered).
   114  func (m *Map) Keys() []interface{} {
   115  	return m.forwardMap.Keys()
   116  }
   117  
   118  // Values returns all values (ordered).
   119  func (m *Map) Values() []interface{} {
   120  	return m.inverseMap.Keys()
   121  }
   122  
   123  // Clear removes all elements from the map.
   124  func (m *Map) Clear() {
   125  	m.forwardMap.Clear()
   126  	m.inverseMap.Clear()
   127  }
   128  
   129  // String returns a string representation of container
   130  func (m *Map) String() string {
   131  	str := "TreeBidiMap\nmap["
   132  	it := m.Iterator()
   133  	for it.Next() {
   134  		str += fmt.Sprintf("%v:%v ", it.Key(), it.Value())
   135  	}
   136  	return strings.TrimRight(str, " ") + "]"
   137  }