Skip to content

Commit

Permalink
Improve performances of v1 (#32)
Browse files Browse the repository at this point in the history
Try to improve the performance of the ordered map without generics. Apply the same approach already used for V2 in commit b46f20e.
  • Loading branch information
MagnaboscoL authored Sep 7, 2022
1 parent b46f20e commit 1e43e19
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 161 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ below, you can use v1.

Internally an `*OrderedMap` uses the composite type
[map](https://go.dev/blog/maps) combined with a
[linked list](https://pkg.go.dev/container/list) to maintain the order.
trimmed down linked list to maintain the order.

## Iterating

Expand Down
33 changes: 0 additions & 33 deletions element.go

This file was deleted.

67 changes: 0 additions & 67 deletions element_test.go

This file was deleted.

95 changes: 95 additions & 0 deletions list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package orderedmap

// Element is an element of a null terminated (non circular) intrusive doubly linked list that contains the key of the correspondent element in the ordered map too.
type Element struct {
// Next and previous pointers in the doubly-linked list of elements.
// To simplify the implementation, internally a list l is implemented
// as a ring, such that &l.root is both the next element of the last
// list element (l.Back()) and the previous element of the first list
// element (l.Front()).
next, prev *Element

// The key that corresponds to this element in the ordered map.
Key interface{}

// The value stored with this element.
Value interface{}
}

// Next returns the next list element or nil.
func (e *Element) Next() *Element {
return e.next
}

// Prev returns the previous list element or nil.
func (e *Element) Prev() *Element {
return e.prev
}

// list represents a null terminated (non circular) intrusive doubly linked list.
// The list is immediately usable after instantiation without the need of a dedicated initialization.
type list struct {
root Element // list head and tail
}

func (l *list) IsEmpty() bool {
return l.root.next == nil
}

// Front returns the first element of list l or nil if the list is empty.
func (l *list) Front() *Element {
return l.root.next
}

// Back returns the last element of list l or nil if the list is empty.
func (l *list) Back() *Element {
return l.root.prev
}

// Remove removes e from its list
func (l *list) Remove(e *Element) {
if e.prev == nil {
l.root.next = e.next
} else {
e.prev.next = e.next
}
if e.next == nil {
l.root.prev = e.prev
} else {
e.next.prev = e.prev
}
e.next = nil // avoid memory leaks
e.prev = nil // avoid memory leaks
}

// PushFront inserts a new element e with value v at the front of list l and returns e.
func (l *list) PushFront(key interface{}, value interface{}) *Element {
e := &Element{Key: key, Value: value}
if l.root.next == nil {
// It's the first element
l.root.next = e
l.root.prev = e
return e
}

e.next = l.root.next
l.root.next.prev = e
l.root.next = e
return e
}

// PushBack inserts a new element e with value v at the back of list l and returns e.
func (l *list) PushBack(key interface{}, value interface{}) *Element {
e := &Element{Key: key, Value: value}
if l.root.prev == nil {
// It's the first element
l.root.next = e
l.root.prev = e
return e
}

e.prev = l.root.prev
l.root.prev.next = e
l.root.prev = e
return e
}
81 changes: 21 additions & 60 deletions orderedmap.go
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
package orderedmap

import "container/list"

type orderedMapElement struct {
key, value interface{}
}

type OrderedMap struct {
kv map[interface{}]*list.Element
ll *list.List
kv map[interface{}]*Element
ll list
}

func NewOrderedMap() *OrderedMap {
return &OrderedMap{
kv: make(map[interface{}]*list.Element),
ll: list.New(),
kv: make(map[interface{}]*Element),
}
}

// Get returns the value for a key. If the key does not exist, the second return
// parameter will be false and the value will be nil.
func (m *OrderedMap) Get(key interface{}) (interface{}, bool) {
value, ok := m.kv[key]
element, ok := m.kv[key]
if ok {
return value.Value.(*orderedMapElement).value, true
return element.Value, true
}

return nil, false
Expand All @@ -33,23 +26,22 @@ func (m *OrderedMap) Get(key interface{}) (interface{}, bool) {
// will be returned. The returned value will be false if the value was replaced
// (even if the value was the same).
func (m *OrderedMap) Set(key, value interface{}) bool {
_, didExist := m.kv[key]

if !didExist {
element := m.ll.PushBack(&orderedMapElement{key, value})
m.kv[key] = element
} else {
m.kv[key].Value.(*orderedMapElement).value = value
_, alreadyExist := m.kv[key]
if alreadyExist {
m.kv[key].Value = value
return false
}

return !didExist
element := m.ll.PushBack(key, value)
m.kv[key] = element
return true
}

// GetOrDefault returns the value for a key. If the key does not exist, returns
// the default value instead.
func (m *OrderedMap) GetOrDefault(key, defaultValue interface{}) interface{} {
if value, ok := m.kv[key]; ok {
return value.Value.(*orderedMapElement).value
if element, ok := m.kv[key]; ok {
return element.Value
}

return defaultValue
Expand All @@ -58,14 +50,9 @@ func (m *OrderedMap) GetOrDefault(key, defaultValue interface{}) interface{} {
// GetElement returns the element for a key. If the key does not exist, the
// pointer will be nil.
func (m *OrderedMap) GetElement(key interface{}) *Element {
value, ok := m.kv[key]
element, ok := m.kv[key]
if ok {
element := value.Value.(*orderedMapElement)
return &Element{
element: value,
Key: element.key,
Value: element.value,
}
return element
}

return nil
Expand All @@ -80,14 +67,10 @@ func (m *OrderedMap) Len() int {
// replaced it will retain the same position. To ensure most recently set keys
// are always at the end you must always Delete before Set.
func (m *OrderedMap) Keys() (keys []interface{}) {
keys = make([]interface{}, m.Len())

element := m.ll.Front()
for i := 0; element != nil; i++ {
keys[i] = element.Value.(*orderedMapElement).key
element = element.Next()
keys = make([]interface{}, 0, m.Len())
for el := m.Front(); el != nil; el = el.Next() {
keys = append(keys, el.Key)
}

return keys
}

Expand All @@ -106,35 +89,13 @@ func (m *OrderedMap) Delete(key interface{}) (didDelete bool) {
// Front will return the element that is the first (oldest Set element). If
// there are no elements this will return nil.
func (m *OrderedMap) Front() *Element {
front := m.ll.Front()
if front == nil {
return nil
}

element := front.Value.(*orderedMapElement)

return &Element{
element: front,
Key: element.key,
Value: element.value,
}
return m.ll.Front()
}

// Back will return the element that is the last (most recent Set element). If
// there are no elements this will return nil.
func (m *OrderedMap) Back() *Element {
back := m.ll.Back()
if back == nil {
return nil
}

element := back.Value.(*orderedMapElement)

return &Element{
element: back,
Key: element.key,
Value: element.value,
}
return m.ll.Back()
}

// Copy returns a new OrderedMap with the same elements.
Expand Down

0 comments on commit 1e43e19

Please sign in to comment.