diff --git a/maps/linkedhashmap/iterator.go b/maps/linkedhashmap/iterator.go index 9ac39318..c2682944 100644 --- a/maps/linkedhashmap/iterator.go +++ b/maps/linkedhashmap/iterator.go @@ -6,7 +6,6 @@ package linkedhashmap import ( "github.com/emirpasic/gods/v2/containers" - "github.com/emirpasic/gods/v2/lists/doublylinkedlist" ) // Assert Iterator implementation @@ -14,15 +13,16 @@ var _ containers.ReverseIteratorWithKey[string, int] = (*Iterator[string, int])( // Iterator holding the iterator's state type Iterator[K comparable, V any] struct { - iterator doublylinkedlist.Iterator[K] - table map[K]V + m *Map[K, V] + index int + element *element[K, V] } // Iterator returns a stateful iterator whose elements are key/value pairs. func (m *Map[K, V]) Iterator() *Iterator[K, V] { return &Iterator[K, V]{ - iterator: m.ordering.Iterator(), - table: m.table, + m: m, + index: -1, } } @@ -31,53 +31,84 @@ func (m *Map[K, V]) Iterator() *Iterator[K, V] { // If Next() was called for the first time, then it will point the iterator to the first element if it exists. // Modifies the state of the iterator. func (iterator *Iterator[K, V]) Next() bool { - return iterator.iterator.Next() + if iterator.index < iterator.m.Size() { + iterator.index++ + } + if !iterator.m.withinRange(iterator.index) { + iterator.element = nil + return false + } + if iterator.index != 0 { + iterator.element = iterator.element.next + } else { + iterator.element = iterator.m.first + } + return true } // Prev moves the iterator to the previous element and returns true if there was a previous element in the container. // If Prev() returns true, then previous element's key and value can be retrieved by Key() and Value(). // Modifies the state of the iterator. func (iterator *Iterator[K, V]) Prev() bool { - return iterator.iterator.Prev() + if iterator.index >= 0 { + iterator.index-- + } + if !iterator.m.withinRange(iterator.index) { + iterator.element = nil + return false + } + if iterator.index == iterator.m.Size()-1 { + iterator.element = iterator.m.last + } else { + iterator.element = iterator.element.prev + } + return iterator.m.withinRange(iterator.index) } // Value returns the current element's value. // Does not modify the state of the iterator. func (iterator *Iterator[K, V]) Value() V { - key := iterator.iterator.Value() - return iterator.table[key] + if iterator.element != nil { + return iterator.element.value + } + var v V + return v } // Key returns the current element's key. // Does not modify the state of the iterator. func (iterator *Iterator[K, V]) Key() K { - return iterator.iterator.Value() + return iterator.element.key } // Begin resets the iterator to its initial state (one-before-first) // Call Next() to fetch the first element if any. func (iterator *Iterator[K, V]) Begin() { - iterator.iterator.Begin() + iterator.index = -1 + iterator.element = nil } // End moves the iterator past the last element (one-past-the-end). // Call Prev() to fetch the last element if any. func (iterator *Iterator[K, V]) End() { - iterator.iterator.End() + iterator.index = iterator.m.Size() + iterator.element = nil } // First moves the iterator to the first element and returns true if there was a first element in the container. // If First() returns true, then first element's key and value can be retrieved by Key() and Value(). // Modifies the state of the iterator func (iterator *Iterator[K, V]) First() bool { - return iterator.iterator.First() + iterator.Begin() + return iterator.Next() } // Last moves the iterator to the last element and returns true if there was a last element in the container. // If Last() returns true, then last element's key and value can be retrieved by Key() and Value(). // Modifies the state of the iterator. func (iterator *Iterator[K, V]) Last() bool { - return iterator.iterator.Last() + iterator.End() + return iterator.Prev() } // 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 { } return false } + +// Check that the index is within bounds of the list +func (m *Map[K, V]) withinRange(index int) bool { + return index >= 0 && index < m.Size() +} diff --git a/maps/linkedhashmap/linkedhashmap.go b/maps/linkedhashmap/linkedhashmap.go index bf5ef4c3..acc0075e 100644 --- a/maps/linkedhashmap/linkedhashmap.go +++ b/maps/linkedhashmap/linkedhashmap.go @@ -15,7 +15,6 @@ import ( "fmt" "strings" - "github.com/emirpasic/gods/v2/lists/doublylinkedlist" "github.com/emirpasic/gods/v2/maps" ) @@ -24,42 +23,78 @@ var _ maps.Map[string, int] = (*Map[string, int])(nil) // Map holds the elements in a regular hash table, and uses doubly-linked list to store key ordering. type Map[K comparable, V any] struct { - table map[K]V - ordering *doublylinkedlist.List[K] + table map[K]*element[K, V] + first *element[K, V] + last *element[K, V] +} + +type element[K comparable, V any] struct { + key K + value V + prev *element[K, V] + next *element[K, V] } // New instantiates a linked-hash-map. func New[K comparable, V any]() *Map[K, V] { return &Map[K, V]{ - table: make(map[K]V), - ordering: doublylinkedlist.New[K](), + table: make(map[K]*element[K, V]), } } // Put inserts key-value pair into the map. // Key should adhere to the comparator's type assertion, otherwise method panics. func (m *Map[K, V]) Put(key K, value V) { - if _, contains := m.table[key]; !contains { - m.ordering.Append(key) + if el, contains := m.table[key]; contains { + el.value = value + } else { + e := &element[K, V]{ + key: key, + value: value, + prev: m.last, + } + + if m.Size() == 0 { + m.first = e + m.last = e + } else { + m.last.next = e + m.last = e + } + m.table[key] = e } - m.table[key] = value } // Get searches the element in the map by key and returns its value or nil if key is not found in tree. // Second return parameter is true if key was found, otherwise false. // Key should adhere to the comparator's type assertion, otherwise method panics. func (m *Map[K, V]) Get(key K) (value V, found bool) { - value, found = m.table[key] - return value, found + element := m.table[key] + if element != nil { + found = true + value = element.value + } + return } // Remove removes the element from the map by key. // Key should adhere to the comparator's type assertion, otherwise method panics. func (m *Map[K, V]) Remove(key K) { - if _, contains := m.table[key]; contains { + if element, contains := m.table[key]; contains { + if element == m.first { + m.first = element.next + } + if element == m.last { + m.last = element.prev + } + if element.prev != nil { + element.prev.next = element.next + } + if element.next != nil { + element.next.prev = element.prev + } + element = nil delete(m.table, key) - index := m.ordering.IndexOf(key) - m.ordering.Remove(index) } } @@ -70,12 +105,19 @@ func (m *Map[K, V]) Empty() bool { // Size returns number of elements in the map. func (m *Map[K, V]) Size() int { - return m.ordering.Size() + return len(m.table) } // Keys returns all keys in-order func (m *Map[K, V]) Keys() []K { - return m.ordering.Values() + keys := make([]K, m.Size()) + count := 0 + it := m.Iterator() + for it.Next() { + keys[count] = it.Key() + count++ + } + return keys } // Values returns all values in-order based on the key. @@ -92,8 +134,9 @@ func (m *Map[K, V]) Values() []V { // Clear removes all elements from the map. func (m *Map[K, V]) Clear() { - clear(m.table) - m.ordering.Clear() + m.table = make(map[K]*element[K, V]) + m.first = nil + m.last = nil } // String returns a string representation of container @@ -104,5 +147,4 @@ func (m *Map[K, V]) String() string { str += fmt.Sprintf("%v:%v ", it.Key(), it.Value()) } return strings.TrimRight(str, " ") + "]" - }