As we know, Go’s built-in map
type does not maintain the order of insertion of elements, and is deliberately set to random when traversing. Therefore, if we want to keep the map in the order in which the elements are inserted, we need to use a third party library, and today we will introduce you to one such library, OrderedMap.
In fact, there are similar data structures in other programming languages, such as LinkedHashMap in java and OrderedDict
in python.
This article describes how to implement such a data type using Go. Note that we are implementing OrderedMap, not SortedMap or TreeMap. SortedMap is traversed in the sorted order of the keys, and we can access the corresponding values one by one by first getting all the keys and sorting them.
But how does OrderedMap
do this if the order of insertion is maintained when it is traversed?
Let’s look at the functionality of OrderedMap
, both to have the functionality of a HashMap to get a key-value pair quickly and to keep the insertion order, then we can achieve this by combining.
- HashMap functionality: Implemented via the built-in map
- Keeping the order of insertion: could have been done with
container/list
, but to support generics we usegithub.com/smallnest/exp/container/list
.
The following is the OrderedMap
data structure, which contains two fields, entries
and list
. entries
is a map whose values are of type *Entry
; the elements in list
are *Entry
and point to the same elements as in the map
.
The definition of Entry
is as follows, in addition to containing Key and Value values, it also contains a list of Element elements, so that it implements a Doubly linked list, where each Entry can find its predecessors and successors.
|
|
When adding and deleting, we need to operate on both fields in order to maintain consistency.
|
|
Get is just a direct query from the map with no loss of performance.
When traversing, the linked list structure is traversed as it maintains the insertion order.
This is the most prominent method of this data structure, but of course it offers more methods such as oldest value, newest value, random traversal, etc., so consider using it if you have the appropriate scenario for your needs.
- type OrderedMap
- func NewOrderedMap(capability int) *OrderedMap[K, V]
- func (m *OrderedMap[K, V]) AddEntries(entries …*Entry[K, V])
- func (m *OrderedMap[K, V]) AddMap(am map[K]V)
- func (m *OrderedMap[K, V]) AddOrderedMap(am OrderedMap[K, V])
- func (m *OrderedMap[K, V]) Clear()
- func (m *OrderedMap[K, V]) Clone() *OrderedMap[K, V]
- func (m *OrderedMap[K, V]) Delete(key K) (val V, existed bool)
- func (m *OrderedMap[K, V]) ForEach(f func(key K, value V) bool)
- func (m *OrderedMap[K, V]) Get(key K) (val V, existed bool)
- func (m *OrderedMap[K, V]) Keys() []K
- func (m *OrderedMap[K, V]) Len() int
- func (m *OrderedMap[K, V]) Load(key K) (V, bool)
- func (m *OrderedMap[K, V]) MarshalJSON() ([]byte, error)
- func (m *OrderedMap[K, V]) Newest() *Entry[K, V]
- func (m *OrderedMap[K, V]) Oldest() *Entry[K, V]
- func (m *OrderedMap[K, V]) Range(f func(key K, value V) bool)
- func (m *OrderedMap[K, V]) Set(key K, value V) (val V, existed bool)
- func (m *OrderedMap[K, V]) Store(key K, value V) (V, bool)
- func (m *OrderedMap[K, V]) UnmarshalJSON(data []byte) error
- func (m *OrderedMap[K, V]) Values() []V
Ref
https://colobu.com/2023/06/18/a-generic-sortedmap-in-go/