omap

package module
v1.0.16 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 16, 2026 License: MIT Imports: 4 Imported by: 0

README

OMAP an Ordered Map

Yet another sorted map in go.. but not really.

Technically the omap package implements very minital btree using a slice. The drivers of the design process, were the performance objectives. The btree implementation is ordered and does not allow for duplicates; The internals manage keys by splicing the internal slice. The side effect of this design results in what operates exactly like ordered map. Under spesific conditions or very large data sets, omap.SliceTree is faster on "Get" operations than the built in go map. An omap.SliceTree instance uses signifigantly less the memory than the map feature in go.

Performance objectives while maintinaing a sorted map:

  • Lookups for both Put and Get operations are always a fixed complexity: o(log n).
  • All iteration operations are fixed cost of o(1).
  • Finding or removing elements between 2 points is always a fixed cost of o(log(n) + log(n)).
  • Finding elements: at, before, or after a given point is always a fixed cost of o(log n)
  • Mass Removal of unordered elements that may or may not exist has a maximum complexity of o(log(n) + log(k) + k)
  • Pre-emptive but predictable growth, this is done by setting the Growth size.

When Should you use omap.SliceTree in place of a map?

Any one of these is a practical use case:

  • An ordered map is required
  • Memory constrained systems
  • Fuzzy logic is required, IE the ability to find points in between keys
  • When a combination of freequent updates and searching by ranges is requried
  • Very large data sets where read speed is more important than write speed
  • Keys that can not be represented as a comparable value
  • When managing elements between ranges is required

Basic usage

The omap package provides a common interface OrderedMap implemented by the following:

Creating ThreadSafe instance Example:

	kv:=NewTs[string,string](cmp.Compare)
	// Save a value
	kv.Put("Hello"," ")
	kv.Put("World","!\n")

	// Itertor
	for k,v :=range kv.All {
		fmt.Printf("%s%s",k,v)
	}

The resulting output will be:

	"Hello World!\n"

We can now make things a bit smaller by removing things by a range.

// Note, both "Sell" and "Universe", were never added to the instance,
// but the between operation works on these keys any ways.
kv.RemoveBetween("Sell","Universe")

// Itertor
for k,v :=range kv.All() {
   fmt.Printf("%s%s\n",k,v)
}

The resulting output will now be:

"Hello \n"

Why this works?

  • The string "Sell" comes before the string "World"
  • The string "Universe" comes after the string "World"

How this works

The index lookup creates 2 values for each potential key:

  • Array postion, example: 0
  • Offset can be any of the following: -1,0,1

Since lookups create both an index position and offsett, it becomes possible to look for the following:

  • Elements before the array
  • Positions between elements of the array
  • Elements after the array
  • Elements to overwrite

API

Constructors

The omap package supports any key type you can provide a compare function for, but a map in go only supports a comparable key type. This means any map can be converted to an OrderedMap instance, but not every OrderedMap instance can be converted to a map.

Function Types Arguments Returns Thread Safe
omap.New K any, V any func(a, b K) int *SliceTree[K, V] false
omap.NewFromMap [K comparable, V any] map[K]V, func(a, b K) int *SliceTree[K, V] false
omap.NewSliceTree K any, V any int, func(a, b K) int *SliceTree[K, V] false
omap.NewTs K any, V any func(a, b K) int OrderedMap[K, V] true
omap.ToMap K comparable, V any OrderedMap[K, V] map[K]V false

As a note, any instance of SliceTree can create a thread safe instance of itself, by calling the s.ToTs() method. If you create a thread safe instance, you should stop using the old instance.

Example conversion from map to a thread safe OrderedMap instance:

s:=omap.NewFromMap(myMap,cb).ToTs()

To check if an instance is thread safe call the s.ThreadSafe() method.

var om OrderedMap[string,int]=New(cb)

if !om.ThreadSafe() {
	om=om.ToTs()
}

Why not always provide a thread safe instance? A thread safe instance requires mutex locking, this limits what can be done even when operations are atomic. Example: You may have a perfectly valid reason to call an iterator from within an iterator on the same instance; This cannot be done when a mutex lock is applied to an existing instance.

OrderedMap Methods

The following table provides a general overview of the methods in OrderedMap.

Method Arguments Teturn types Description
All iter.Seq2[K, V] iterator for all Keys and Values
Keys iter.Seq[K] iterator for all keys
Values iter.Seq[V] iterator for all Values
Exists key K bool true if the key was found
Contains key K bool true if the key is between both the FirstKey and LastKey
Put key K, value V int Sets the key and value pair
Get key K value V, ok bool Returned the value for the key if ok is true
Remove key K value V, ok bool If ok is true, the returned value was removed based on the given key
RemoveAll int Clears all elements and returns how many elements were removed
MassRemove keys ...K int Tries to remove all keys provided, returns how many keys were removed
MassRemoveKV keys ...K iter.Seq2[int, K] Tries to remove all keys provided. The iterator with a copy of all key value pairs that were removed
Size int returns the number of key/value pairs in the instance
FirstKey key K, ok bool When ok is true the first key in the instance is returned
LastKey key K, ok bool When ok is true the last key in the instance is returned
Between a,b K, opt ...int total int Returns the number of elements between a and b. For options See
BetweenKV a,b K, opt ...int iter.Seq2[int, K] Returns an iterator that contains the key/value pairs between a and b. For options See
RemoveBetween a,b K, opt ...int int Returns the number of elements removed between a and b. For options See
RemoveBetweenKV a,b K, opt ...int iter.Seq2[int, K] Returns an iterator that contains the key/value pairs that were moved from between a and b. For options See
ThreadSafe bool Returns true if this instance is thread safe
Merge set OrderedMap[K, V] int Merges set into this instance
SetOverwrite cb func(key K, oldValue, newValue V) Sets the callback method that fires before a value is overwritten
SetGrowth grow int Sets the internal growth value for the slice
ToTs() OrderedMap[K, V] If this instance is not contained in a thread safe wrapper, returns this instance in a thread safe wrapper, other wise returns this instance
Between Options

The following table exlains the usage and possible values for functions that support between operations.

opt[id] Options Description
0 omap.FIRST_KEY When set, the a field is ignored and s.FirstKey is used in its place
0 omap.LAST_KEY When set, the b field is ignored and s.LastKey is used in its place
0 omap.FIRST_KEY|omap.LAST_KEY This causes both a and b to be ignored

Example using s.BetweenKV:

  for k,v :=s.BetweenKV("","Tomorrow",omap.FIRSTKEY) {
    fmt.Printf("Key: [%s], Value: [%d]\n")
  }

Returns all values up to "Tomorrow".

Benchmarks

So benchmarks are always very subjective, but the real question is: what do we compare omap too? The only real answer is the native map in go. Now this is in no way a fair comparison.. The omap package can use any data set, so long as a compare function can be provided, while the map in go only needs to be optimized internally for hashing bytes, so we would expected the native map feature to be an order of magnitude faster.

Disclamier: omap.SliceTree is built around a Compare function, this means the benchmark requires creating a proxy key that is equal to the key provided by the Cmp function. In this benchmark the key used for the map has to be generated from the base string, and the original string pointer and value then need to be saved off in an additional data structure, this gives us a like for like compare between the native go map feature and omap.SliceTree.

How well does omap compare native map feature in go?:

BenchmarkNew/Native_map_Put,_keys:_[1600]-10                               26467             44379 ns/op           93056 B/op       1606 allocs/op
BenchmarkNew/Slicetree,_Put,_keys:_[1600]-10                                9781            114559 ns/op           41008 B/op          2 allocs/op
BenchmarkNew/Native_map,_Get,_keys:_[1600]-10                              10000            109108 ns/op               0 B/op          0 allocs/op
BenchmarkNew/SliceTree,_Get,_keys:_[1600]-10                                7849            150049 ns/op               0 B/op          0 allocs/op
BenchmarkNew/Native_map,_Count_between,_keys:_[1600]-10                       67          17179143 ns/op           22552 B/op       2944 allocs/op
BenchmarkNew/SliceTree,_Count_Between_nodes_keys:_[1600]-10                 4364            267578 ns/op               0 B/op          0 allocs/op
BenchmarkNew/Native_map_Put,_keys:_[2500]-10                               15544             77330 ns/op          169264 B/op       2510 allocs/op
BenchmarkNew/Slicetree,_Put,_keys:_[2500]-10                                5958            186216 ns/op           65584 B/op          2 allocs/op
BenchmarkNew/Native_map,_Get,_keys:_[2500]-10                               5620            200192 ns/op               0 B/op          0 allocs/op
BenchmarkNew/SliceTree,_Get,_keys:_[2500]-10                                5156            231305 ns/op               0 B/op          0 allocs/op
BenchmarkNew/Native_map,_Count_between,_keys:_[2500]-10                       27          41700145 ns/op           36988 B/op       4744 allocs/op
BenchmarkNew/SliceTree,_Count_Between_nodes_keys:_[2500]-10                 2847            425970 ns/op               0 B/op          0 allocs/op
BenchmarkNew/Native_map_Put,_keys:_[3600]-10                               10038            119693 ns/op          304880 B/op       3618 allocs/op
BenchmarkNew/Slicetree,_Put,_keys:_[3600]-10                                3668            302038 ns/op           90160 B/op          2 allocs/op
BenchmarkNew/Native_map,_Get,_keys:_[3600]-10                               2881            373605 ns/op               0 B/op          0 allocs/op
BenchmarkNew/SliceTree,_Get,_keys:_[3600]-10                                3358            351491 ns/op               0 B/op          0 allocs/op
BenchmarkNew/Native_map,_Count_between,_keys:_[3600]-10                        9         114708859 ns/op           54710 B/op       6944 allocs/op
BenchmarkNew/SliceTree,_Count_Between_nodes_keys:_[3600]-10                 1914            627752 ns/op               0 B/op          0 allocs/op
BenchmarkNew/Native_map_Put,_keys:_[4900]-10                                7454            159133 ns/op          336080 B/op       4918 allocs/op
BenchmarkNew/Slicetree,_Put,_keys:_[4900]-10                                2288            480656 ns/op          122928 B/op          2 allocs/op
BenchmarkNew/Native_map,_Get,_keys:_[4900]-10                                764           1435220 ns/op               0 B/op          0 allocs/op
BenchmarkNew/SliceTree,_Get,_keys:_[4900]-10                                2323            499635 ns/op               0 B/op          0 allocs/op
BenchmarkNew/Native_map,_Count_between,_keys:_[4900]-10                        4         270712341 ns/op           75738 B/op       9545 allocs/op
BenchmarkNew/SliceTree,_Count_Between_nodes_keys:_[4900]-10                 1357            881794 ns/op               0 B/op          0 allocs/op

How to read the benchmark

So what do these numbers really tell us? Well nothing we didn't all ready know prior to the benchmark. The map feature of go trades memory for read and write speed, in particular on wirte. Usually platforms are more cpu constrained than memory constrained, but that isn't always the case.

On write:

  • Go map is faster

On read:

  • Go map is faster with smaller sets of keys
  • omap.SliceTree is faster on larger sets of keys

Where the native map in go always perfoms worse is in memory usage:

  • Go map uses about 45%-70% more memory than omap.SliceTree.

Scanning for any key between 2 strings:

  • Go map must be iterated over and each key must be compared o(n)
  • omap.SliceTree is just o(log(n)+log(n))

So which is better for performance? Depends.. If you need very large sets of keys and you would have to create a proxy key to represent the raw key, then use omap.SliceTree, other wise use map.

Scanning all keys for every key is a known worst case.. so why include it? People do it, and most ordered map packages on pkg.go.dev will force you do do that at least until you reach the end of your range.

Why does the native go map read slow down so much after 2500 elements?

Simple: memory bandwidth, omap.SliceTree for the same kind of work, uses between 45%-70% less memory than a native map in go. The inner workings of omap.SliceTree is a single slice in memory. This means memory reads for SliceTree are usually contiguous. Although the native go map hashing operation is cheaper when it comes to cpu cycles. omap.SliceTree is built entierly around a single slice and can often times stay entierly inside the cpu cache on reads. Even when going to main memory, a slice is usually a sequential set of reads, which greatly improve memory read performance.

Why is SliceTree always slower on write?

Simple: memory bandwidth. omap.SliceTree uses a slice in memory and the elemetns are copied back and forth in blocks. Although the lookup and storage uses less memory, the write operation involes splicing an array, which will go to main memory more often. The very thing that gives omap.SliceTree its read speed at scale slows it down in write operations.

So which is better SliceTree or a map?

Very subjective, omap.Slicetree is built entirly around being able to find a range of keys without scanning. A map in go is built around hashing bytes. After a certan point omap.Slicetree will always read faster, but the go native map feature will aways write faster. The go map implementation will always use more memory than omap.SliceTree, but the native go map perfoms sligtly less than half the memory modification of omap.SliceTree on insertion/update.

Why include memory in benchmarks?

This is a complex topic, but here is a short answer: Try turning memory benchmarks on for other ordered map pacakges on pkg.go.dev, they use orders of magnitued more memory than the native map in go. Most ordered map implementations arn't performacne competative with the native map in go. The omap.SliceTree is at least competative with the native go map implementation. In spesific use cases omap.SliceTree is signifigantly faster than the native map feature of go. An omap.SliceTree instance does all this while being an ordered map, that is no small feat.

Comapring go map o(1) and omap.SliceTee o(log(n))

Go map: o(1) How so? In truth go map uses the first 2 bytes as keys in a 2 tier tree, the remaing bytes then hit the o(1) or full scan. This is the sweet spot on most use cases. The side effect is, keys are never going to be ordered.

omap.Slicetree is o(log n)? The omap.SliceTree is a btree with inifinite depth, indexed by sequence order. An omap.SliceTree instance is never a full scan, but depth of search is always more expensive in smaller sets and always cheaper in larger sets. Effectivly omap.SliceTree is pure an Order First search with the root at the median of the tree. This hits the sweet spot for range based lookups and massive data sets. The side effect is an ordered index of keys.

Odd Quirks of indexing

So which is faster a 1 byte key using a map or omap.SliceTree?

  • at a one byte key omap.SliceTree is faster

So which is faster a 2 byte key using a map in go with 65535 elements or omap.SliceTree?

  • at 2 bytes a normal map in go is faster

So when does omap.Slicetree actually become faster?

  • when a map in go would end up with colisions on the 3rd tier, this causes a full scan of that tier.
  • when does that happen? Usually after a few thousand unique strings, but it depends on which buckets they land.

Is omap.SliceTree ever faster with ints or floats?

  • Yes on range scans
  • On Writes, Never
  • On Read, never on a16 bit number
  • On Read after: the first 16 bits cause a collision and ternary logic is faster operating on the entire integer or float value

Documentation

Overview

Yet another sorted map in go.. but not really.

Technically the omap package implements very minital btree using a slice. The drivers of the design process, were the performance objectives. The btree implementation is ordered and does not allow for duplicates; The internals manage keys by splicing the internal slice. The side effect of this design results in what operates exactly like ordered map. Under spesific conditions or very large data sets, omap.SliceTree is faster on "Get" operations than the built in go map. An omap.SliceTree instance uses signifigantly less the memory than the map feature in go.

Performance objectives while maintinaing a sorted map:

  • Lookups for both Put and Get operations are always a fixed complexity: o(log n).
  • All iteration operations are fixed cost of o(1).
  • Finding or removing elements between 2 points is always a fixed cost of o(log(n) + log(n)).
  • Finding elements: at, before, or after a given point is always a fixed cost of o(log n)
  • Mass Removal of unordered elements that may or may not exist has a maximum complexity of o(log(n) + log(k) + k)
  • Pre-emptive but predictable growth, this is done by setting the Growth size.

The omap package provides a common interface OrderedMap implemented by the following:

Basic Example:

kv:=New[string,string](cmp.Compare)
// Save a value
kv.Put("Hello"," ")
kv.Put("World","!\n")

// Itertor
for k,v :=range kv.All {
	fmt.Printf("%s%s",k,v)
}

The resulting output will be:

"Hello World!\n"

We can now make things a bit smaller by removing things by a range.

kv.RemoveBetween("Sell","Universe")

// Itertor
for k,v :=range kv.All {
	fmt.Printf("%s%s\n",k,v)
}

The resulting output will now be:

"Hello \n"

The index lookup creates 2 values for each potential key:

  • Array postion, example: 0
  • Offset can be any of the following: -1,0,1

Since lookups create both an index position and offsett, it becomes possible to look for the following:

  • Elements before the array
  • Positions between elements of the array
  • Elements after the array
  • Elements to overwrite

Index

Constants

View Source
const (
	FIRST_KEY = 1
	LAST_KEY  = 2
)

Variables

This section is empty.

Functions

func GetIndex

func GetIndex[K any, V any](k K, Cmp func(a, b K) int, Slices []KvSet[K, V]) (index, offset int)

Returns the index and offset of a given key.

The index is the current relative postion in the slice.

The offset represents where the item would be placed:

  • offset of 0, at index value.
  • offset of 1, expand the slice after the inddex and put the value to the right of the index
  • offset of -1, expand the slice before the index and put the value to left of the current postion

Complexity: o(log n)

func KvIter

func KvIter[K any, V any](set []KvSet[K, V]) iter.Seq2[K, V]

func Merge

func Merge[K comparable, V any](dst OrderedMap[K, V], src map[K]V) int

Merges a map into an OrderedMap instance.

func ToMap

func ToMap[K comparable, V any](src OrderedMap[K, V]) map[K]V

Utility method, to convert from an OrderedMap instance to a regular map. Due to constraints placed on maps in go, this feature is implemented as a function, not a method.

func TsKvIterWrapper

func TsKvIterWrapper[K any, V any](seq iter.Seq2[K, V]) iter.Seq2[K, V]

Takes an existing K,V iterator and returns a thread safe version.

Types

type KvSet

type KvSet[K any, V any] struct {
	Key   K
	Value V
}

type OrderedMap

type OrderedMap[K any, V any] interface {
	// Should return an iterator for all key/value pairs
	All() iter.Seq2[K, V]

	// Returns all keys, the int just expected to be a sequential number
	Keys() iter.Seq[K]

	// Returns all the values, the int is expected to be a sequential number
	Values() iter.Seq[V]

	// Returns true if the key exists, false if it does not.
	Exists(key K) bool

	// Returns true if the key is between the first and last key
	Contains(key K) bool

	// Sets the key to the value, returns the index id.
	Put(key K, value V)

	// Tries to get the value using the given key.
	// If the key exists, found is set to true, if the key does not exists then found is set to false.
	Get(key K) (value V, found bool)

	// Tries to remove the given key, returns value is set if ok is true.
	Remove(key K) (value V, ok bool)

	// Removes all elements.
	// Returns how many element were removed.
	RemoveAll() int

	// Attempts to remove all keys, returns the number of keys removed.
	MassRemove(keys ...K) (total int)

	// Attempts to remove all keys, returns an iterator with the key/value pairs
	MassRemoveKV(keys ...K) iter.Seq2[K, V]

	// Returns the current number of key/value pairs.
	Size() int

	// If ok is true, returns the first key.
	FirstKey() (key K, ok bool)

	// If ok is true, returns the last key.
	LastKey() (key K, ok bool)

	// Returns total number of elements between a and b.
	// Neither a or b are required to exist.
	//
	// The the optional opt argument:
	//  - When: opt[0]==omap.FIRST_KEY, a is ignored and the FirstKey is used.
	//  - When: opt[0]==omap.LAST_KEY, b is ignored and the LastKey is used.
	//  - To Ignore both a and b, set: opt[0]==omap.FIRST_KEY|omap.LAST_KEY.
	Between(a, b K, opt ...int) (total int)

	// Returns an iterator that contains the key value sets between a and b.
	// Neither a or b are required to exist.
	//
	// The the optional opt argument:
	//  - When: opt[0]==omap.FIRST_KEY, a is ignored and the FirstKey is used.
	//  - When: opt[0]==omap.LAST_KEY, b is ignored and the LastKey is used.
	//  - To Ignore both a and b, set: opt[0]==omap.FIRST_KEY|omap.LAST_KEY.
	BetweenKV(a, b K, opt ...int) (seq iter.Seq2[K, V])

	// Trys to delete the elements between a and b, returns the total number of elements deleted.
	// Neither a or b are required to exist.
	//
	// The the optional opt argument:
	//  - When: opt[0]==omap.FIRST_KEY, a is ignored and the FirstKey is used.
	//  - When: opt[0]==omap.LAST_KEY, b is ignored and the LastKey is used.
	//  - To Ignore both a and b, set: opt[0]==omap.FIRST_KEY|omap.LAST_KEY.
	RemoveBetween(a, b K, opt ...int) (total int)

	// Trys to delete the elements between a and b, returns an iterator that contains removed K,V pairs.
	// Neither a or b are required to exist.
	//
	// The the optional opt argument:
	RemoveBetweenKV(a, b K, opt ...int) (seq iter.Seq2[K, V])

	// Should return true if the instance is thread safe, fakse if not.
	ThreadSafe() bool

	// Merges a given [OrderedMap] into this one.
	// Returns the number of keys added
	Merge(set OrderedMap[K, V]) int

	// Sets the overwrite notice.  Its good to know when things change!
	SetOverwrite(cb func(key K, oldValue, newValue V))

	// Sets the growth value.
	SetGrowth(grow int)

	// Returns a thread safe instance.
	// If the instance is all ready thread safe, then the current instance is returned.
	ToTs() OrderedMap[K, V]
}

func NewTs

func NewTs[K any, V any](Cmp func(a, b K) int) (Map OrderedMap[K, V])

Creates a new thread safe OrderedMap instance.

type SliceTree

type SliceTree[K any, V any] struct {

	// Internally managed keys slice
	Slices []KvSet[K, V]

	// Compare function.
	Cmp func(a, b K) int

	// Required non 0 value, determins by what capacity we grow the internal
	// Slice.  Default is 1.
	Growth int

	// Required non nil value, called when ever a value is overwritten.
	// Seting this funtion saves on having to write a check when data is overwritten.
	OnOverWrite func(key K, oldValue V, newValue V)
}

func New

func New[K any, V any](cb func(a, b K) int) *SliceTree[K, V]

Creates a new SliceTee with the default Slices size of 100. If you require more control over the starting size of the slice use the NewSliceTree function in stead.

func NewFromMap

func NewFromMap[K comparable, V any](m map[K]V, cb func(a, b K) int) *SliceTree[K, V]

func NewSliceTree

func NewSliceTree[K any, V any](size int, cb func(a, b K) int) *SliceTree[K, V]

Creatss a new SliceTree with the internal Slice set to "size".

func (*SliceTree[K, V]) All

func (s *SliceTree[K, V]) All() iter.Seq2[K, V]

Returns an iterator for key/value pars. The internals of this iterator do not lock the tree or prevent updates. You can safely call an iterator from with an iterator. and not run into deadlocks.

func (*SliceTree[K, V]) Between

func (s *SliceTree[K, V]) Between(a, b K, opt ...int) (total int)

Between implements OrderedMap

func (*SliceTree[K, V]) BetweenKV

func (s *SliceTree[K, V]) BetweenKV(a, b K, opt ...int) (seq iter.Seq2[K, V])

BetweenKV implements OrderedMap

func (*SliceTree[K, V]) Contains

func (s *SliceTree[K, V]) Contains(key K) bool

func (*SliceTree[K, V]) Exists

func (s *SliceTree[K, V]) Exists(k K) bool

Returns true if the k exists in the slcie. Complexity: o(log n)

func (*SliceTree[K, V]) FirstKey

func (s *SliceTree[K, V]) FirstKey() (key K, ok bool)

GetFirstKey implements OrderedMap

func (*SliceTree[K, V]) Get

func (s *SliceTree[K, V]) Get(k K) (value V, found bool)

Tries to fetch value based on key of k, if k does not exist, found is false.

func (*SliceTree[K, V]) Keys

func (s *SliceTree[K, V]) Keys() iter.Seq[K]

Returns an iterator for the current keys. The internals of this iterator do not lock the tree or prevent updates. You can safely call an iterator from with an iterator. and not run into deadlocks.

func (*SliceTree[K, V]) LastKey

func (s *SliceTree[K, V]) LastKey() (key K, ok bool)

GetLastKey implements OrderedMap

func (*SliceTree[K, V]) MassRemove

func (s *SliceTree[K, V]) MassRemove(args ...K) (total int)

Attempts to remove the keys from the tree in bulk. Returns the number of keys removed.

This is almost always faster than just looping over a list of keys and calling Remove one key at a time. The internals of The MassRemove method deletes elements in squential contiguys blocks: reducing on the number of internal splice operations.

Complexity:

Worst case shown (Per key removed or k): o(log(n) + o(log k) + 2*k).

In truth, the real world complexity is drastically reduced by the following:

  • Deletetion of duplicate keys.
  • Keys being deleted do not exist.

The complexity is defined by the steps required:

  • Index lookups: o(log n) +k
  • child index is created to de-duplicate and order keys for deletion: o(log k)
  • key deletion is done in contiguous blocks: k

func (*SliceTree[K, V]) MassRemoveKV

func (s *SliceTree[K, V]) MassRemoveKV(args ...K) iter.Seq2[K, V]

func (*SliceTree[K, V]) Merge

func (s *SliceTree[K, V]) Merge(set OrderedMap[K, V]) int

Merge implements OrderedMap

func (*SliceTree[K, V]) Put

func (s *SliceTree[K, V]) Put(k K, v V)

Sets the key/vale pair and returns the index id. Comlexity: o(log n)

func (*SliceTree[K, V]) Remove

func (s *SliceTree[K, V]) Remove(k K) (value V, ok bool)

Tries to remove the element of k, returns false if it fails. Complexity: o(log n)

func (*SliceTree[K, V]) RemoveAll

func (s *SliceTree[K, V]) RemoveAll() int

Removes all elements in the slice, but keeps the memory allocated.

func (*SliceTree[K, V]) RemoveBetween

func (s *SliceTree[K, V]) RemoveBetween(a, b K, opt ...int) (total int)

RemoveBetween implements OrderedMap

func (*SliceTree[K, V]) RemoveBetweenKV

func (s *SliceTree[K, V]) RemoveBetweenKV(a, b K, opt ...int) (removed iter.Seq2[K, V])

RemoveBetween implements OrderedMap

func (*SliceTree[K, V]) Set

func (s *SliceTree[K, V]) Set(index int, v V) (status bool)

Sets the value in the index to v. The last index value returned from Put to update the last index point. This lets you bypass the o(log n) update complexity for writing to the same element over and over again. The internals still call s.OnOverWrite for you.

func (*SliceTree[K, V]) SetGrowth

func (s *SliceTree[K, V]) SetGrowth(grow int)

SetGrowth implements OrderedMap

func (*SliceTree[K, V]) SetIndex

func (s *SliceTree[K, V]) SetIndex(idx, offset int, k K, v V) (index int)

Sets the given k,v pair based on the index and offset provided by a call to GetIndex. Returns the resulting array index id.

Using a combinaiton of GetIndex and SetIndex lets you bypass the o(log n) comlexity when wiring to the same node over and over again. The value reutrned from Put can be used to update the internals using SetIndex with the offset being 0.

func (*SliceTree[K, V]) SetOverwrite

func (s *SliceTree[K, V]) SetOverwrite(cb func(key K, oldValue, newValue V))

Sets the internal OnOverWrite function.

func (*SliceTree[K, V]) Size

func (s *SliceTree[K, V]) Size() int

Returns the total number key/value pairs in the slice.

func (*SliceTree[K, V]) ThreadSafe

func (s *SliceTree[K, V]) ThreadSafe() bool

Returns false.

func (*SliceTree[K, V]) ToTs

func (s *SliceTree[K, V]) ToTs() OrderedMap[K, V]

Returns a thread safe instnace from the current instance.

func (*SliceTree[K, V]) UnSafeMassRemove

func (s *SliceTree[K, V]) UnSafeMassRemove(keys ...K)

This method is by defenition, unsafe, but fast.

Only use if the keys being removed meet all of the following requirements:

  • no duplicate keys
  • keys are in ascending ordered
  • all keys currently exist in the internals of the tree

Complexity: key=keys; o(log n +k)

func (*SliceTree[K, V]) Values

func (s *SliceTree[K, V]) Values() iter.Seq[V]

Returns an iterator for the current values The internals of this iterator do not lock the tree or prevent updates. You can safely call an iterator from with an iterator. and not run into deadlocks.

type ThreadSafeOrderedMap

type ThreadSafeOrderedMap[K any, V any] struct {
	// Instance to wrap for locking
	Tree OrderedMap[K, V]
	// contains filtered or unexported fields
}

func (*ThreadSafeOrderedMap[K, V]) All

func (s *ThreadSafeOrderedMap[K, V]) All() iter.Seq2[K, V]

All implements OrderedMap.

func (*ThreadSafeOrderedMap[K, V]) Between

func (s *ThreadSafeOrderedMap[K, V]) Between(a, b K, opt ...int) (total int)

Between implements OrderedMap

func (*ThreadSafeOrderedMap[K, V]) BetweenKV

func (s *ThreadSafeOrderedMap[K, V]) BetweenKV(a, b K, opt ...int) (seq iter.Seq2[K, V])

BetweenKV implements OrderedMap

func (*ThreadSafeOrderedMap[K, V]) Contains

func (s *ThreadSafeOrderedMap[K, V]) Contains(key K) bool

func (*ThreadSafeOrderedMap[K, V]) Exists

func (s *ThreadSafeOrderedMap[K, V]) Exists(key K) bool

Exists implements OrderedMap.

func (*ThreadSafeOrderedMap[K, V]) FirstKey

func (s *ThreadSafeOrderedMap[K, V]) FirstKey() (key K, ok bool)

GetFirstKey implements OrderedMap

func (*ThreadSafeOrderedMap[K, V]) Get

func (s *ThreadSafeOrderedMap[K, V]) Get(key K) (value V, found bool)

Get implements OrderedMap.

func (*ThreadSafeOrderedMap[K, V]) Keys

func (s *ThreadSafeOrderedMap[K, V]) Keys() iter.Seq[K]

Keys implements OrderedMap.

func (*ThreadSafeOrderedMap[K, V]) LastKey

func (s *ThreadSafeOrderedMap[K, V]) LastKey() (key K, ok bool)

GetLastKey implements OrderedMap

func (*ThreadSafeOrderedMap[K, V]) MassRemove

func (s *ThreadSafeOrderedMap[K, V]) MassRemove(keys ...K) (total int)

MassRemove implements OrderedMap.

func (*ThreadSafeOrderedMap[K, V]) MassRemoveKV

func (s *ThreadSafeOrderedMap[K, V]) MassRemoveKV(keys ...K) iter.Seq2[K, V]

func (*ThreadSafeOrderedMap[K, V]) Merge

func (s *ThreadSafeOrderedMap[K, V]) Merge(set OrderedMap[K, V]) int

Merge implements OrderedMap

func (*ThreadSafeOrderedMap[K, V]) Put

func (s *ThreadSafeOrderedMap[K, V]) Put(key K, value V)

Put implements OrderedMap.

func (*ThreadSafeOrderedMap[K, V]) Remove

func (s *ThreadSafeOrderedMap[K, V]) Remove(key K) (V, bool)

Remove implements OrderedMap.

func (*ThreadSafeOrderedMap[K, V]) RemoveAll

func (s *ThreadSafeOrderedMap[K, V]) RemoveAll() int

RemoveAll implements OrderedMap.

func (*ThreadSafeOrderedMap[K, V]) RemoveBetween

func (s *ThreadSafeOrderedMap[K, V]) RemoveBetween(a, b K, opt ...int) (total int)

RemoveBetween implements OrderedMap.

func (*ThreadSafeOrderedMap[K, V]) RemoveBetweenKV

func (s *ThreadSafeOrderedMap[K, V]) RemoveBetweenKV(a, b K, opt ...int) (seq iter.Seq2[K, V])

RemoveBetweenKV implements OrderedMap.

func (*ThreadSafeOrderedMap[K, V]) SetGrowth

func (s *ThreadSafeOrderedMap[K, V]) SetGrowth(grow int)

func (*ThreadSafeOrderedMap[K, V]) SetOverwrite

func (s *ThreadSafeOrderedMap[K, V]) SetOverwrite(cb func(key K, oldValue, newValue V))

SetOverwrite implements OrderedMap

func (*ThreadSafeOrderedMap[K, V]) Size

func (s *ThreadSafeOrderedMap[K, V]) Size() int

Size implements OrderedMap.

func (*ThreadSafeOrderedMap[K, V]) ThreadSafe

func (s *ThreadSafeOrderedMap[K, V]) ThreadSafe() bool

Always returns true.

func (*ThreadSafeOrderedMap[K, V]) ToTs

func (s *ThreadSafeOrderedMap[K, V]) ToTs() OrderedMap[K, V]

Always returns this instance.

func (*ThreadSafeOrderedMap[K, V]) Values

func (s *ThreadSafeOrderedMap[K, V]) Values() iter.Seq[V]

Values implements OrderedMap.

Directories

Path Synopsis
examples
Iterators command
hello command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL