Skip to content

Commit 2f00422

Browse files
committed
perf: speed-up linkedhashmap
1 parent 8323d02 commit 2f00422

File tree

2 files changed

+110
-32
lines changed

2 files changed

+110
-32
lines changed

maps/linkedhashmap/iterator.go

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,23 @@ package linkedhashmap
66

77
import (
88
"github.com/emirpasic/gods/v2/containers"
9-
"github.com/emirpasic/gods/v2/lists/doublylinkedlist"
109
)
1110

1211
// Assert Iterator implementation
1312
var _ containers.ReverseIteratorWithKey[string, int] = (*Iterator[string, int])(nil)
1413

1514
// Iterator holding the iterator's state
1615
type Iterator[K comparable, V any] struct {
17-
iterator doublylinkedlist.Iterator[K]
18-
table map[K]V
16+
m *Map[K, V]
17+
index int
18+
element *element[K, V]
1919
}
2020

2121
// Iterator returns a stateful iterator whose elements are key/value pairs.
2222
func (m *Map[K, V]) Iterator() *Iterator[K, V] {
2323
return &Iterator[K, V]{
24-
iterator: m.ordering.Iterator(),
25-
table: m.table,
24+
m: m,
25+
index: -1,
2626
}
2727
}
2828

@@ -31,53 +31,84 @@ func (m *Map[K, V]) Iterator() *Iterator[K, V] {
3131
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
3232
// Modifies the state of the iterator.
3333
func (iterator *Iterator[K, V]) Next() bool {
34-
return iterator.iterator.Next()
34+
if iterator.index < iterator.m.Size() {
35+
iterator.index++
36+
}
37+
if !iterator.m.withinRange(iterator.index) {
38+
iterator.element = nil
39+
return false
40+
}
41+
if iterator.index != 0 {
42+
iterator.element = iterator.element.next
43+
} else {
44+
iterator.element = iterator.m.first
45+
}
46+
return true
3547
}
3648

3749
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
3850
// If Prev() returns true, then previous element's key and value can be retrieved by Key() and Value().
3951
// Modifies the state of the iterator.
4052
func (iterator *Iterator[K, V]) Prev() bool {
41-
return iterator.iterator.Prev()
53+
if iterator.index >= 0 {
54+
iterator.index--
55+
}
56+
if !iterator.m.withinRange(iterator.index) {
57+
iterator.element = nil
58+
return false
59+
}
60+
if iterator.index == iterator.m.Size()-1 {
61+
iterator.element = iterator.m.last
62+
} else {
63+
iterator.element = iterator.element.prev
64+
}
65+
return iterator.m.withinRange(iterator.index)
4266
}
4367

4468
// Value returns the current element's value.
4569
// Does not modify the state of the iterator.
4670
func (iterator *Iterator[K, V]) Value() V {
47-
key := iterator.iterator.Value()
48-
return iterator.table[key]
71+
if iterator.element != nil {
72+
return iterator.element.value
73+
}
74+
var v V
75+
return v
4976
}
5077

5178
// Key returns the current element's key.
5279
// Does not modify the state of the iterator.
5380
func (iterator *Iterator[K, V]) Key() K {
54-
return iterator.iterator.Value()
81+
return iterator.element.key
5582
}
5683

5784
// Begin resets the iterator to its initial state (one-before-first)
5885
// Call Next() to fetch the first element if any.
5986
func (iterator *Iterator[K, V]) Begin() {
60-
iterator.iterator.Begin()
87+
iterator.index = -1
88+
iterator.element = nil
6189
}
6290

6391
// End moves the iterator past the last element (one-past-the-end).
6492
// Call Prev() to fetch the last element if any.
6593
func (iterator *Iterator[K, V]) End() {
66-
iterator.iterator.End()
94+
iterator.index = iterator.m.Size()
95+
iterator.element = nil
6796
}
6897

6998
// First moves the iterator to the first element and returns true if there was a first element in the container.
7099
// If First() returns true, then first element's key and value can be retrieved by Key() and Value().
71100
// Modifies the state of the iterator
72101
func (iterator *Iterator[K, V]) First() bool {
73-
return iterator.iterator.First()
102+
iterator.Begin()
103+
return iterator.Next()
74104
}
75105

76106
// Last moves the iterator to the last element and returns true if there was a last element in the container.
77107
// If Last() returns true, then last element's key and value can be retrieved by Key() and Value().
78108
// Modifies the state of the iterator.
79109
func (iterator *Iterator[K, V]) Last() bool {
80-
return iterator.iterator.Last()
110+
iterator.End()
111+
return iterator.Prev()
81112
}
82113

83114
// NextTo moves the iterator to the next element from current position that satisfies the condition given by the
@@ -107,3 +138,8 @@ func (iterator *Iterator[K, V]) PrevTo(f func(key K, value V) bool) bool {
107138
}
108139
return false
109140
}
141+
142+
// Check that the index is within bounds of the list
143+
func (m *Map[K, V]) withinRange(index int) bool {
144+
return index >= 0 && index < m.Size()
145+
}

maps/linkedhashmap/linkedhashmap.go

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"fmt"
1616
"strings"
1717

18-
"github.com/emirpasic/gods/v2/lists/doublylinkedlist"
1918
"github.com/emirpasic/gods/v2/maps"
2019
)
2120

@@ -24,42 +23,78 @@ var _ maps.Map[string, int] = (*Map[string, int])(nil)
2423

2524
// Map holds the elements in a regular hash table, and uses doubly-linked list to store key ordering.
2625
type Map[K comparable, V any] struct {
27-
table map[K]V
28-
ordering *doublylinkedlist.List[K]
26+
table map[K]*element[K, V]
27+
first *element[K, V]
28+
last *element[K, V]
29+
}
30+
31+
type element[K comparable, V any] struct {
32+
key K
33+
value V
34+
prev *element[K, V]
35+
next *element[K, V]
2936
}
3037

3138
// New instantiates a linked-hash-map.
3239
func New[K comparable, V any]() *Map[K, V] {
3340
return &Map[K, V]{
34-
table: make(map[K]V),
35-
ordering: doublylinkedlist.New[K](),
41+
table: make(map[K]*element[K, V]),
3642
}
3743
}
3844

3945
// Put inserts key-value pair into the map.
4046
// Key should adhere to the comparator's type assertion, otherwise method panics.
4147
func (m *Map[K, V]) Put(key K, value V) {
42-
if _, contains := m.table[key]; !contains {
43-
m.ordering.Append(key)
48+
if el, contains := m.table[key]; contains {
49+
el.value = value
50+
} else {
51+
e := &element[K, V]{
52+
key: key,
53+
value: value,
54+
prev: m.last,
55+
}
56+
57+
if m.Size() == 0 {
58+
m.first = e
59+
m.last = e
60+
} else {
61+
m.last.next = e
62+
m.last = e
63+
}
64+
m.table[key] = e
4465
}
45-
m.table[key] = value
4666
}
4767

4868
// Get searches the element in the map by key and returns its value or nil if key is not found in tree.
4969
// Second return parameter is true if key was found, otherwise false.
5070
// Key should adhere to the comparator's type assertion, otherwise method panics.
5171
func (m *Map[K, V]) Get(key K) (value V, found bool) {
52-
value, found = m.table[key]
53-
return value, found
72+
element := m.table[key]
73+
if element != nil {
74+
found = true
75+
value = element.value
76+
}
77+
return
5478
}
5579

5680
// Remove removes the element from the map by key.
5781
// Key should adhere to the comparator's type assertion, otherwise method panics.
5882
func (m *Map[K, V]) Remove(key K) {
59-
if _, contains := m.table[key]; contains {
83+
if element, contains := m.table[key]; contains {
84+
if element == m.first {
85+
m.first = element.next
86+
}
87+
if element == m.last {
88+
m.last = element.prev
89+
}
90+
if element.prev != nil {
91+
element.prev.next = element.next
92+
}
93+
if element.next != nil {
94+
element.next.prev = element.prev
95+
}
96+
element = nil
6097
delete(m.table, key)
61-
index := m.ordering.IndexOf(key)
62-
m.ordering.Remove(index)
6398
}
6499
}
65100

@@ -70,12 +105,19 @@ func (m *Map[K, V]) Empty() bool {
70105

71106
// Size returns number of elements in the map.
72107
func (m *Map[K, V]) Size() int {
73-
return m.ordering.Size()
108+
return len(m.table)
74109
}
75110

76111
// Keys returns all keys in-order
77112
func (m *Map[K, V]) Keys() []K {
78-
return m.ordering.Values()
113+
keys := make([]K, m.Size())
114+
count := 0
115+
it := m.Iterator()
116+
for it.Next() {
117+
keys[count] = it.Key()
118+
count++
119+
}
120+
return keys
79121
}
80122

81123
// Values returns all values in-order based on the key.
@@ -92,8 +134,9 @@ func (m *Map[K, V]) Values() []V {
92134

93135
// Clear removes all elements from the map.
94136
func (m *Map[K, V]) Clear() {
95-
clear(m.table)
96-
m.ordering.Clear()
137+
m.table = make(map[K]*element[K, V])
138+
m.first = nil
139+
m.last = nil
97140
}
98141

99142
// String returns a string representation of container
@@ -104,5 +147,4 @@ func (m *Map[K, V]) String() string {
104147
str += fmt.Sprintf("%v:%v ", it.Key(), it.Value())
105148
}
106149
return strings.TrimRight(str, " ") + "]"
107-
108150
}

0 commit comments

Comments
 (0)