├── .github └── workflows │ ├── check-markdown-links.yml │ ├── go.yml │ └── gosec.yml ├── .gitignore ├── LICENSE ├── README.md ├── README_zh.md ├── binary_search.go ├── binary_search_test.go ├── builtin_set.go ├── builtin_set_test.go ├── check.bat ├── check.sh ├── compare.go ├── compare_test.go ├── compute.go ├── compute_test.go ├── container.go ├── container_test.go ├── dlist.go ├── dlist_queue.go ├── dlist_queue_test.go ├── dlist_test.go ├── doc.go ├── functor.go ├── generate.go ├── generate_test.go ├── generated_doc.md ├── go.mod ├── go.sum ├── heap.go ├── heap.md ├── heap_bench_test.go ├── heap_test.go ├── helper.go ├── iterator.go ├── lookup.go ├── lookup_test.go ├── mlc_config.json ├── pool.go ├── pool_test.go ├── priority_queue.go ├── priority_queue_test.go ├── skiplist.go ├── skiplist.md ├── skiplist_bench_test.go ├── skiplist_newnode.go ├── skiplist_newnode_generate.sh ├── skiplist_set.go ├── skiplist_set_test.go ├── skiplist_test.go ├── slist.go ├── slist_test.go ├── sort.go ├── sort_test.go ├── stack.go ├── stack_test.go ├── test_helper.go ├── transform.go ├── transform_fillzero_clear.go ├── transform_fillzero_old.go ├── transform_test.go ├── types.go ├── updatedoc.bat ├── updatedoc.sh ├── vector.go └── vector_test.go /.github/workflows/check-markdown-links.yml: -------------------------------------------------------------------------------- 1 | name: Check Markdown links 2 | 3 | on: push 4 | 5 | jobs: 6 | markdown-link-check: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@master 10 | - uses: gaurav-nelson/github-action-markdown-link-check@v1 11 | with: 12 | use-quiet-mode: 'yes' 13 | check-modified-files-only: 'yes' 14 | config-file: mlc_config.json 15 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v3 18 | with: 19 | go-version: 1.18 20 | 21 | - name: Build 22 | run: go build -v ./... 23 | 24 | - name: Test 25 | run: go test -v ./... -covermode=count -coverprofile=coverage.out.tmp 26 | 27 | - name: Filter out test_helper 28 | run: grep -E -v "/(test_helper)\.go:" coverage.out.tmp > coverage.out 29 | 30 | - name: Convert coverage.out to coverage.lcov 31 | uses: jandelgado/gcov2lcov-action@v1.0.9 32 | 33 | - name: Coveralls 34 | uses: coverallsapp/github-action@v1.1.2 35 | with: 36 | github-token: ${{ secrets.github_token }} 37 | path-to-lcov: coverage.lcov 38 | -------------------------------------------------------------------------------- /.github/workflows/gosec.yml: -------------------------------------------------------------------------------- 1 | name: Run Gosec 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | jobs: 10 | tests: 11 | runs-on: ubuntu-latest 12 | env: 13 | GO111MODULE: on 14 | steps: 15 | - name: Checkout Source 16 | uses: actions/checkout@v2 17 | - name: Run Gosec Security Scanner 18 | uses: securego/gosec@master 19 | with: 20 | args: ./... 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | __debug_bin 8 | 9 | # Test binary, built with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | *.tmp 15 | *.bak 16 | *.old 17 | 18 | # Dependency directories (remove the comment below to include it) 19 | # vendor/ 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stl4go -- STL for Golang 2 | 3 | English | [简体中文](README_zh.md) 4 | 5 | This library contains generic containers and algorithms, it is designed to be STL for Golang. 6 | 7 | [![License Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-red.svg)](COPYING) 8 | [![Golang](https://img.shields.io/badge/Language-go1.18+-blue.svg)](https://go.dev/) 9 | ![Build Status](https://github.com/chen3feng/stl4go/actions/workflows/go.yml/badge.svg) 10 | [![Coverage Status](https://coveralls.io/repos/github/chen3feng/stl4go/badge.svg?branch=master)](https://coveralls.io/github/chen3feng/stl4go?branch=master) 11 | [![GoReport](https://goreportcard.com/badge/github.com/securego/gosec)](https://goreportcard.com/report/github.com/chen3feng/stl4go) 12 | [![Go Reference](https://pkg.go.dev/badge/github.com/chen3feng/stl4go.svg)](https://pkg.go.dev/github.com/chen3feng/stl4go) 13 | 14 | This library depends on go generics, which is introduced in 1.18+. 15 | 16 | 17 | 18 | 19 | 20 | ```go 21 | import "github.com/chen3feng/stl4go" 22 | ``` 23 | 24 | Package stl4go is a generic container and algorithm library for go. 25 | 26 | ## Introduce 27 | 28 | This library is a general container and algorithm library that attempts to learn from the C++ STL implementation after Go 1.18 began to support generics. 29 | (Personally I's totally unacceptable for me use to languages without generics, so I didn't try it until go 1.18). 30 | 31 | The code quality of this library is quite high and follows the latest best practices in the industry. 32 | Test coverage is close💯%, ✅,CI, and [gosec](https://securego.io/) check are both set up, got 33 | [![GoReport](https://goreportcard.com/badge/github.com/securego/gosec)](https://goreportcard.com/report/github.com/chen3feng/stl4go) score。 34 | 35 | ## Features 36 | 37 | As we all know, C++'s STL includes containers, algorithms, and iterators relate the two. 38 | 39 | Due to language limitations, it is impossible and unnecessary to completely imitate the interface of C++ STL in Go, 40 | so C++ users may feel familiar, and sometimes (maybe) feel more convenient. 41 | 42 | ### Containers 43 | 44 | There are following container interfaces: 45 | 46 | - `Container` is the base interface for all containers 47 | - `Map` is a key-value associative container 48 | - `Set` is set container 49 | - `SortedMap` is a ordered key-value associative container 50 | - `SortedSet` is a ordered set container 51 | - `Queue` is a FIFO Queue 52 | - `Deque` is a double ended queue 53 | 54 | Different interface has different methods. The `Container` interface has following methods: 55 | 56 | - `IsEmpty() bool` returns whether the container is empty 57 | - `Len() int` returns the number of elements in the container 58 | - `Clear()` to clear the container 59 | 60 | Read [source code](container.go) for details. 61 | 62 | Currently container implementations are: 63 | 64 | - [x] `BuiltinSet` provided a set funtionality based on Go's own `map`. It provides basic operations such as insert, 65 | search and remove, as well as advanced functions such as union, intersection, difference, subset, superset, and disjoint. 66 | - [x] `Vector` is a thin encapsulation based on `slice`. It provides functions such as insertion and deletion in the middle, range deletion, etc., 67 | and is still compatible with slices. 68 | - [x] `DList` is a doubly linked list, supports push/popup at both ending. 69 | - [x] `SList` is a singly linked list, supports push/popup at the head and push at the tail. 70 | - [x] [SkipList](skiplist.md) is an ordered associative container that fills the gap where Go `map` only supports unordered. 71 | This is currently the fastest skip list I tested in GitHub, see [skiplist-survey](https://github.com/chen3feng/skiplist-survey) for performance comparison 72 | - [x] `SkipList` is a `SortedSet` container based on the skiplist. 73 | - [x] `Stack`, is a FILO container based on Slice implementation 74 | - [x] `DListQueue` is a bidirectional FIFO queue, implemented based on linked list. 75 | - [x] `PriorityQuque` is a priority queue based on heap. Much easier to use and faster than [container/heap](https://pkg.go.dev/container/heap). 76 | 77 | ### Non-Container Components 78 | 79 | - [x] `Pool` A type safe Pool, is implemented based on `sync.Pool`. 80 | 81 | ### Iterators 82 | 83 | Vector, DList and SkipList support iterators. 84 | 85 | ```go 86 | // Iterator is the interface for container's iterator. 87 | type Iterator[T any] interface { 88 | IsNotEnd() bool // Whether it is point to the end of the range. 89 | MoveToNext() // Let it point to the next element. 90 | Value() T // Return the value of current element. 91 | } 92 | 93 | // MutableIterator is the interface for container's mutable iterator. 94 | type MutableIterator[T any] interface { 95 | Iterator[T] 96 | Pointer() *T // Return the pointer to the value of current element. 97 | } 98 | ``` 99 | 100 | ```go 101 | l := stl4go.NewDListOf(Range(1, 10000)...) 102 | sum := 0 103 | for i := 0; i < b.N; i++ { 104 | for it := l.Iterate(); it.IsNotEnd(); it.MoveToNext() { 105 | sum += it.Value() 106 | } 107 | } 108 | ``` 109 | 110 | The iterator of SkipList is `MutableMapIterator`: 111 | 112 | ```go 113 | // MapIterator is the interface for map's iterator. 114 | type MapIterator[K any, V any] interface { 115 | Iterator[V] 116 | Key() K // The key of the element 117 | } 118 | 119 | // MutableMapIterator is the interface for map's mutable iterator. 120 | type MutableMapIterator[K any, V any] interface { 121 | MutableIterator[V] 122 | Key() K // The key of the element 123 | } 124 | ``` 125 | 126 | SkipList also supports range iteration: 127 | 128 | ```go 129 | sl := stl4go.NewSkipList[int, int]() 130 | for i := 0; i < 1000; i++ { 131 | sl.Insert(i, 0) 132 | } 133 | it := sl.FindRange(120, 350) 134 | ``` 135 | 136 | Iterating over `it` only yields the keys between 120 and 349. 137 | 138 | In many cases, it is more convenient to use the `ForEach` and `ForEachIf` methods provided by the container, 139 | and the performance is often better: 140 | 141 | ```go 142 | func TestSkipList_ForEach(t *testing.T) { 143 | sl := newSkipListN(100) 144 | a := []int{} 145 | sl.ForEach(func(k int, v int) { 146 | a = append(a, k) 147 | }) 148 | expectEq(t, len(a), 100) 149 | expectTrue(t, IsSorted(a)) 150 | } 151 | ``` 152 | 153 | `ForEachIf` is used for scenarios that you want to end early during the iteration: 154 | 155 | ```go 156 | func Test_DList_ForEachIf(t *testing.T) { 157 | l := NewDListOf(1, 2, 3) 158 | c := 0 159 | l.ForEachIf(func(n int) bool { 160 | c = n 161 | return n != 2 162 | }) 163 | expectEq(t, c, 2) 164 | } 165 | ``` 166 | 167 | You can use `ForEachMutable` or `ForEachMutable` to modify the value of an element during the iteration: 168 | 169 | ```go 170 | func TestSkipList_ForEachMutable(t *testing.T) { 171 | sl := newSkipListN(100) 172 | sl.ForEachMutable(func(k int, v *int) { 173 | *v = -*v 174 | }) 175 | for i := 0; i < sl.Len(); i++ { 176 | expectEq(t, *sl.Find(i), -i) 177 | } 178 | } 179 | ``` 180 | 181 | ### Algorithms 182 | 183 | Due to the limitations of language, most algorithms only support Slice. 184 | The functions name of the algorithms ends with `If` or `Func`, 185 | indicating that a custom comparison function can be passed. 186 | 187 | #### Generate 188 | 189 | - `Range` returns a Slice of contains integers in the range of `[begin, end)` 190 | - `Generate` generates a sequence with the given function to fill the Slice 191 | 192 | #### Data manipulation 193 | 194 | - `Copy` return a copies of specified slice 195 | - `CopyTo` copies all elements in slice a to slice to, return the copied slice. 196 | - `Fill` repeatedly fills a slice with the specified value 197 | - `FillZero` fills each element in slice a with zero value. 198 | - `FillPattern` repeatedly fills a slice with the specified pattern 199 | - `Replace` replaces every element that equals to old with new 200 | - `ReplaceIf` replaces every element that make preq returns true with new 201 | - `Transform` passes the value at each position of the slice to the specified function and sets it back with its return value 202 | - `TransformTo` passes the value at each position of slice `a` to the specified function, 203 | sets its return value to the corresponding position in slice `b`, and returns a slice of corresponding length of slice `b` 204 | - `TransformCopy` passes the value at each position of the slice to the specified function, 205 | sets its return value to the corresponding position in a new slice and returns 206 | - `Unique` removes adjacent duplicate elements from a slice and returns a slice with new length containing the remaining elements, 207 | `UniqueCopy` returns a copy without modifying the original slice 208 | - `Remove` removes all elements in the slice equal to the specified value, `RemoveCopy` returns a copy without modifying the original slice 209 | - `RemoveIf` removes all elements in the slice that are equivalent to making the specified function return `true`, 210 | `RemoveIfCopy` does not modify the original slice but returns a copy 211 | - `Shuffle` random shuffle elements in the slice 212 | - `Reverse` reverses a slice, `ReverseCopy` returns a copy without modifying the original slice 213 | 214 | #### Compute 215 | 216 | - `Sum` Sum 217 | - `SumAs` sums and returns a result as another type (eg. use `int64` to return the sum of `[]int32`). 218 | - `Average` finds the average value. 219 | - `AverageAs` averages and returns the result as another type (eg. use `float64` to return the sum of `[]int`). 220 | - `Count` returns the number equivalent to the specified value 221 | - `CountIf` returns the number of elements for which the specified function returns `true` 222 | 223 | #### Compare 224 | 225 | - `Equal` checks whether two sequences are equal 226 | - `Compare` compares two sequences and returns `-1`, `0`, and `1` in lexicographical order, respectively indicating the relationship of 2 slices. 227 | 228 | #### Lookup 229 | 230 | - `Min`, `Max` find the maximum and minimum 231 | - `MinN`, `MaxN`, `MinMax` return the maximum and minimum values in the slice 232 | - `Find` linearly finds the first specified value and returns its index 233 | - `FindIf` linearly finds the first value that make specified function returns `true` and returns its index 234 | - `AllOf`, `AnyOf`, `NoneOf` return whether all, any, or none of the elements in the range can make the passed function return `true` accordingly. 235 | 236 | #### Binary Search 237 | 238 | See C++ STL. 239 | 240 | - `BinarySearch` 241 | - `LowerBound` 242 | - `UpperBound` 243 | 244 | #### Sort 245 | 246 | - `Sort` sorting 247 | - `DescSort` descending sorting 248 | - `StableSort` stable sorting 249 | - `DescStableSort` descending stable sorting 250 | - `IsSorted` check whether the slice is sorted 251 | - `IsDescSorted` check whether the slice is sorted in descending order 252 | 253 | #### heap 254 | 255 | [Heap](heap.md) provides basic min heap algorithms: 256 | 257 | - `MakeMinHeap` Convert a slice to a min heap 258 | - `IsMinHeap` Check whether a slice is a min heap 259 | - `PushMinHeap` Pushes an element in to the heap 260 | - `PopMinHeap` Popups an element from the top of the heap 261 | - `RemoveMinHeap` Removes an element at index from the heap 262 | 263 | and variants with custome comparasion function: 264 | 265 | - `MakeHeapFunc` 266 | - `IsHeapFunc` 267 | - `PushHeapFunc` 268 | - `PopHeapFunc` 269 | - `RemoveHeapFunc` 270 | 271 | both of them are mush faster and easier to use than [container/heap](https://pkg.go.dev/container/heap). 272 | 273 | See detailed usage and benchmark report in the [document of heap](heap.md)。 274 | 275 | ### Interface Design and Naming 276 | 277 | The design leart much from the C++ STL. The `T` here represents `template`. Yes, Go's generic is not template. but who made C++ so influential and STL so famous? 278 | 279 | Many libraries are designed for small code repositories or split into multiple subpackages in one repository. 280 | For example: 281 | 282 | ```go 283 | import ( 284 | "github.com/someone/awesomelib/skiplist" 285 | "github.com/someone/awesomelib/binarysearch" 286 | ) 287 | 288 | func main() { 289 | sl := skiplist.New() 290 | } 291 | ``` 292 | 293 | This way of writing seems elegant, but because everyone likes good names, import renaming has to be introduced in use in case of package name conflict, 294 | and different users have different renaming style, which increases the mental burden of code reading and writing. 295 | 296 | I don't like this style, especially in a larger repository. 297 | 298 | Therefore, this library is all under the `stl4go` package, and it is expected that it will not namesake in other people's libraries. 299 | 300 | ### TODO 301 | 302 | See [Issue](https://github.com/chen3feng/stl4go/issues)。 303 | 304 | And add more detailed documents. 305 | 306 | ## Go Doc 307 | 308 | Click to view the [generated doc](generated_doc.md). 309 | 310 | ## Reference 311 | 312 | - [C++ STL](https://en.wikipedia.org/wiki/Standard_Template_Library) 313 | - [liyue201/gostl](https://github.com/liyue201/gostl) 314 | - [zyedidia/generic](https://github.com/zyedidia/generic) 315 | - [hlccd/goSTL](https://github.com/hlccd/goSTL) 316 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | # stl4go -- Go 语言的 STL 2 | 3 | [English](README.md) | 简体中文 4 | 5 | 本库包含 Go 语言实现的泛型容器和算法库,就像 C++ 中的 STL。 6 | 7 | [![License Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-red.svg)](COPYING) 8 | [![Golang](https://img.shields.io/badge/Language-go1.18+-blue.svg)](https://go.dev/) 9 | ![Build Status](https://github.com/chen3feng/stl4go/actions/workflows/go.yml/badge.svg) 10 | [![Coverage Status](https://coveralls.io/repos/github/chen3feng/stl4go/badge.svg?branch=master)](https://coveralls.io/github/chen3feng/stl4go?branch=master) 11 | [![GoReport](https://goreportcard.com/badge/github.com/securego/gosec)](https://goreportcard.com/report/github.com/chen3feng/stl4go) 12 | [![Go Reference](https://pkg.go.dev/badge/github.com/chen3feng/stl4go.svg)](https://pkg.go.dev/github.com/chen3feng/stl4go) 13 | 14 | ```go 15 | import "github.com/chen3feng/stl4go" 16 | ``` 17 | 18 | ## 简介 19 | 20 | 本库是在 Go 1.18 开始支持泛型后,尝试借鉴 C++ STL 实现的一个通用容器和算法库。(我个人完全无法接受没有泛型的语言,因此直到 go 1.18 才开始尝试用它) 21 | 22 | 本库代码质量高,遵循了业界最新的最佳实践。测试覆盖率接近 💯%,✅,设置了 CI、 [gosec](https://securego.io/) 检查, 23 | [![GoReport](https://goreportcard.com/badge/github.com/securego/gosec)](https://goreportcard.com/report/github.com/chen3feng/stl4go) 评分。 24 | 25 | ## 主要功能 26 | 27 | 众所周知,C++ 的 STL 包括容器、算法,并以迭代器关联两者。 28 | 29 | 受语言限制,在 Go 中无法也没有必要完全模仿 C++的接口,因此 C++ 用户可能会感觉似曾相识相识,有时候也会感觉更方便。 30 | 31 | ### 容器 32 | 33 | 定义了如下容器接口: 34 | 35 | - `Container` 是所有容器的基础接口 36 | - `Map` 定义了 key-value 关联容器的接口 37 | - `Set` 定义了集合容器的接口 38 | - `SortedMap` 定义了有序 key-value 关联容器的接口 39 | - `SortedSet` 定义了有序集合容器的接口 40 | - `Queue` 定义了先进先出的队列的接口 41 | - `Deque` 定义了双端队列的接口 42 | 43 | 不同的容器接口支持的方法不同,下面是 `Container` 接口的方法: 44 | 45 | - `IsEmpty() bool` 返回容器是否为空 46 | - `Len() int` 返回容器中的元素个数 47 | - `Clear()` 清空容器 48 | 49 | 具体请参考[源代码](container.go)。 50 | 51 | 提供的具体容器实现有: 52 | 53 | - [x] `BuiltinSet` 是基于 Go 自己的 map 封装的集合。提供了插入查找删除等基本操作,以及并集、交集、差集、子集、超集、不交集等高级功能。 54 | - [x] `Vector` 是基于切片封装的向量。提供了中间插入删除、区间删除等功能,依然与切片兼容。 55 | - [x] `DList` 是双链表容器,支持两端插入删除。 56 | - [x] `SList` 是单链表容器,支持头部插入删除及尾部插入。 57 | - [x] [跳表(SkipList)](skiplist.md) 是一种有序的关联容器,可以填补 Go `map` 只支持无序的的空白。这是目前全 GitHub 最快的跳表,参见 [skiplist-survey](https://github.com/chen3feng/skiplist-survey)的性能比较 58 | - [x] `SkipListSet` 是基于跳表实现的有序集合容器 59 | - [x] `Stack`,栈基于 Slice 实现 60 | - [x] `DListQueue` 双向进出的队列,基于双链表实现 61 | - [x] `PriorityQuque` 优先队列,基于堆实现,比 [container/heap](https://pkg.go.dev/container/heap) 更易用而且快不少。 62 | 63 | ### 非容器组件 64 | 65 | - [x] `Pool` 类型安全的 Pool,基于对 `sync.Pool` 的封装而实现。 66 | 67 | ### 迭代器 68 | 69 | Vector、DList 和 SkipList 支持迭代器。 70 | 71 | ```go 72 | // Iterator is the interface for container's iterator. 73 | type Iterator[T any] interface { 74 | IsNotEnd() bool // Whether it is point to the end of the range. 75 | MoveToNext() // Let it point to the next element. 76 | Value() T // Return the value of current element. 77 | } 78 | 79 | // MutableIterator is the interface for container's mutable iterator. 80 | type MutableIterator[T any] interface { 81 | Iterator[T] 82 | Pointer() *T // Return the pointer to the value of current element. 83 | } 84 | ``` 85 | 86 | ```go 87 | l := stl4go.NewDListOf(Range(1, 10000)...) 88 | sum := 0 89 | for i := 0; i < b.N; i++ { 90 | for it := l.Iterate(); it.IsNotEnd(); it.MoveToNext() { 91 | sum += it.Value() 92 | } 93 | } 94 | ``` 95 | 96 | SkipList 的迭代器是 `MutableMapIterator`: 97 | 98 | ```go 99 | // MapIterator is the interface for map's iterator. 100 | type MapIterator[K any, V any] interface { 101 | Iterator[V] 102 | Key() K // The key of the element 103 | } 104 | 105 | // MutableMapIterator is the interface for map's mutable iterator. 106 | type MutableMapIterator[K any, V any] interface { 107 | MutableIterator[V] 108 | Key() K // The key of the element 109 | } 110 | ``` 111 | 112 | SkipList 还支持区间迭代: 113 | 114 | ```go 115 | sl := stl4go.NewSkipList[int, int]() 116 | for i := 0; i < 1000; i++ { 117 | sl.Insert(i, 0) 118 | } 119 | it := sl.FindRange(120, 350) 120 | ``` 121 | 122 | 对 `it` 迭代可以只会得到 120~349 之间的数。 123 | 124 | 更多时候,使用容器提供的 `ForEach` 和 `ForEachIf` 更方便,往往性能也更好一些: 125 | 126 | ```go 127 | func TestSkipList_ForEach(t *testing.T) { 128 | sl := newSkipListN(100) 129 | a := []int{} 130 | sl.ForEach(func(k int, v int) { 131 | a = append(a, k) 132 | }) 133 | expectEq(t, len(a), 100) 134 | expectTrue(t, IsSorted(a)) 135 | } 136 | ``` 137 | 138 | `ForEachIf` 用于遍历时候提前结束的场景: 139 | 140 | ```go 141 | func Test_DList_ForEachIf(t *testing.T) { 142 | l := NewDListOf(1, 2, 3) 143 | c := 0 144 | l.ForEachIf(func(n int) bool { 145 | c = n 146 | return n != 2 147 | }) 148 | expectEq(t, c, 2) 149 | } 150 | ``` 151 | 152 | 使用 `ForEachMutable` 或 `ForEachMutable` 可以在遍历时候修改元素的值: 153 | 154 | ```go 155 | func TestSkipList_ForEachMutable(t *testing.T) { 156 | sl := newSkipListN(100) 157 | sl.ForEachMutable(func(k int, v *int) { 158 | *v = -*v 159 | }) 160 | for i := 0; i < sl.Len(); i++ { 161 | expectEq(t, *sl.Find(i), -i) 162 | } 163 | } 164 | ``` 165 | 166 | ### 算法 167 | 168 | 受语言功能限制,绝大部分算法只支持 Slice。算法的函数名以 `If`、`Func` 结尾的,表示可以传递一个自定义的比较函数。 169 | 170 | #### 生成型 171 | 172 | - Range 返回一个 [begin, end) 的整数构成的 Slice 173 | - Generate 用给定的函数生成一个序列填充到 Slice 中 174 | 175 | #### 数据操作 176 | 177 | - `Copy` 返回切片的拷贝 178 | - `CopyTo` 拷贝切片的内容到另一个切片 179 | - `Fill` 用指定的值重复填充一个切片 180 | - `FillZero` 用类型的零值重复填充一个切片 181 | - `FillPattern` 用指定的模式重复填充一个切片 182 | - `Replace` 替换所有等于指定值的元素为新值 183 | - `ReplaceIf` 替换所有让函数返回 `true` 的元素为新值 184 | - `Transform` 把切片的每个位置的值传给指定的函数,用其返回值设置回去 185 | - `TransformTo` 把切片 a 的每个位置的值传给指定的函数,将其返回值设置到切片 b 中相应的位置,并返回 b 的相应长度的切片 186 | - `TransformCopy` 把切片的每个位置的值传给指定的函数,将其返回值设置到一个新的切片中相应的位置并返回 187 | - `Unique` 去除切片中相邻的重复元素,返回包含剩余元素的新长度的切片,`UniqueCopy` 则不修改原切片而是返回一个拷贝 188 | - `Remove` 去除切片中等于指定值的所有元素,`RemoveCopy` 则不修改原切片而是返回一个拷贝 189 | - `RemoveIf` 去除切片中等于让指定函数返回 `true` 的所有元素,`RemoveIfCopy` 则不修改原切片而是返回一个拷贝 190 | - `Shuffle` 随机洗牌 191 | - `Reverse` 反转一个切片,`ReverseCopy` 则不修改原切片而是返回一个拷贝 192 | 193 | #### 计算型 194 | 195 | - `Sum` 求和 196 | - `SumAs` 求和并以另一种类型的结果返回(比如以 `int64` 类型返回 `[]int32` 的和) 197 | - `Average` 求平均值。 198 | - `AverageAs` 求平均值并以另一种类型的结果返回(比如 `float64` 返回 `[]int` 的和) 199 | - `Count` 返回和指定值相当的个数 200 | - `CountIf` 返回让指定函数返回 `true` 的元素的个数 201 | 202 | #### 比较 203 | 204 | - `Equal` 判断两个序列是否相等 205 | - `Compare` 比较两个序列,按字典序返回 -1、0、1 分别表示起大小关系 206 | 207 | #### 查找 208 | 209 | - `Min`, `Max` 求最大最小值 210 | - `MinN`、`MaxN`、`MinMax` 返回 slice 中的最大和最小值 211 | - `Find` 线性查找第一个指定的值,返回其下标 212 | - `FindIf` 线性查找指定函数返回 `true` 的值,返回其下标 213 | - `AllOf`、`AnyOf`、`NoneOf` 返回区间中是否全部、任何一个、没有一个元素能使传入的函数返回 `true` 214 | 215 | #### 二分查找 216 | 217 | 参考 C++STL。 218 | 219 | - `BinarySearch` 220 | - `LowerBound` 221 | - `UpperBound` 222 | 223 | #### 排序 224 | 225 | - `Sort` 升序排序 226 | - `DescSort` 降序排序 227 | - `StableSort` 升序稳定排序 228 | - `DescStableSort` 降序稳定排序 229 | - `IsSorted` 是否是升序排序的 230 | - `IsDescSorted` 是否是降序排序的 231 | 232 | #### 堆 233 | 234 | 提供基本的堆算法: 235 | 236 | - `MakeMinHeap` 在一个切片上构造出一个最小堆 237 | - `IsMinHeap` 判断一个切片是不是一个最小堆 238 | - `PushMinHeap` 把一个元素压入最小堆 239 | - `PopMinHeap` 弹出堆顶的元素 240 | - `RemoveMinHeap` 从切片的指定位置删除一个元素 241 | 242 | 以及相应的自定义比较函数的版本: 243 | 244 | - `MakeHeapFunc` 245 | - `IsHeapFunc` 246 | - `PushHeapFunc` 247 | - `PopHeapFunc` 248 | - `RemoveHeapFunc` 249 | 250 | 都比 go 标准库 [container/heap](https://pkg.go.dev/container/heap) 更容易使用且更快。 251 | 252 | 用法和测试详情参见[heap的文档](heap.md)。 253 | 254 | ### 接口设计和命名 255 | 256 | 较多地参考了 C++ STL。T 表示模板。是的,Go 的泛型不是模板,但是谁让 C++ 那么影响力,而 STL 又那么有名呢。 257 | 258 | 很多库的设计采用小的代码仓库或者一个仓库中拆分成多个子包。 259 | 260 | 比如 261 | 262 | ```go 263 | import ( 264 | "github.com/someone/awesomelib/skiplist" 265 | "github.com/someone/awesomelib/binarysearch" 266 | ) 267 | 268 | func main() { 269 | sl := skiplist.New() 270 | } 271 | ``` 272 | 273 | 这种写法看似优雅,但是由于好的名字大家都喜欢,在使用中又不得不引入 import 重命名,而不同的使用者别名不一样,增加代码读写的心智负担。 274 | 275 | 我不太喜欢这种风格。 276 | 277 | 因此本库全部在 `stl4go` 包下,期望不会和别人的库重名。 278 | 279 | ### TODO 280 | 281 | 参见 [Issue](https://github.com/chen3feng/stl4go/issues)。 282 | 283 | 以及更详细的文档。 284 | 285 | ## Go Doc 286 | 287 | 点击查看[生成的文档](generated_doc.md). 288 | 289 | ## Reference 290 | 291 | - [C++ STL](https://en.wikipedia.org/wiki/Standard_Template_Library) 292 | - [liyue201/gostl](https://github.com/liyue201/gostl) 293 | - [zyedidia/generic](https://github.com/zyedidia/generic) 294 | - [hlccd/goSTL](https://github.com/hlccd/goSTL) 295 | -------------------------------------------------------------------------------- /binary_search.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | // LowerBound returns an index to the first element in the ascending ordered slice a that 4 | // does not satisfy element < value (i.e. greater or equal to), 5 | // or len(a) if no such element is found. 6 | // 7 | // Complexity: O(log(len(a))). 8 | func LowerBound[T Ordered](a []T, value T) int { 9 | loc := 0 10 | count := len(a) 11 | for count > 0 { 12 | i := loc 13 | step := count / 2 14 | i += step 15 | if a[i] < value { 16 | i++ 17 | loc = i 18 | count -= step + 1 19 | } else { 20 | count = step 21 | } 22 | } 23 | return loc 24 | } 25 | 26 | // LowerBoundFunc returns an index to the first element in the ordered slice a that 27 | // does not satisfy less(element, value)), or len(a) if no such element is found. 28 | // 29 | // The elements in the slice a should sorted according with compare func less. 30 | // 31 | // Complexity: O(log(len(a))). 32 | func LowerBoundFunc[T any](a []T, value T, less LessFn[T]) int { 33 | loc := 0 34 | count := len(a) 35 | for count > 0 { 36 | i := loc 37 | step := count / 2 38 | i += step 39 | if less(a[i], value) { 40 | i++ 41 | loc = i 42 | count -= step + 1 43 | } else { 44 | count = step 45 | } 46 | } 47 | return loc 48 | } 49 | 50 | // UpperBound returns an index to the first element in the ascending ordered slice a such that 51 | // value < element (i.e. strictly greater), or len(a) if no such element is found. 52 | // 53 | // Complexity: O(log(len(a))). 54 | func UpperBound[T Ordered](a []T, value T) int { 55 | loc := 0 56 | count := len(a) 57 | for count > 0 { 58 | i := loc 59 | step := count / 2 60 | i += step 61 | if !(value < a[i]) { 62 | i++ 63 | loc = i 64 | count -= step + 1 65 | } else { 66 | count = step 67 | } 68 | } 69 | return loc 70 | } 71 | 72 | // UpperBoundFunc returns an index to the first element in the ordered slice a such that 73 | // less(value, element)) is true (i.e. strictly greater), or len(a) if no such element is found. 74 | // 75 | // The elements in the slice a should sorted according with compare func less. 76 | // 77 | // Complexity: O(log(len(a))). 78 | func UpperBoundFunc[T any](a []T, value T, less LessFn[T]) int { 79 | loc := 0 80 | count := len(a) 81 | for count > 0 { 82 | i := loc 83 | step := count / 2 84 | i += step 85 | if !less(value, a[i]) { 86 | i++ 87 | loc = i 88 | count -= step + 1 89 | } else { 90 | count = step 91 | } 92 | } 93 | return loc 94 | } 95 | 96 | // BinarySearch returns the (index, true) to the first element in the ascending ordered slice a 97 | // such that element == value, or (-1, false) if no such element is found. 98 | // 99 | // Complexity: O(log(len(a))). 100 | func BinarySearch[T Ordered](a []T, value T) (index int, ok bool) { 101 | loc := LowerBound(a, value) 102 | if loc < len(a) && a[loc] == value { 103 | return loc, true 104 | } 105 | return -1, false 106 | } 107 | 108 | // BinarySearchFunc returns the (index, true) to the first element in the ordered slice a such that 109 | // less(element, value) and less(value, element) are both false, 110 | // or (-1, false) if no such element is found. 111 | // 112 | // The elements in the slice a should sorted according with compare func less. 113 | // 114 | // Complexity: O(log(len(a))). 115 | func BinarySearchFunc[T any](a []T, value T, less LessFn[T]) (index int, ok bool) { 116 | loc := LowerBoundFunc(a, value, less) 117 | if loc < len(a) && !less(value, a[loc]) { 118 | return loc, true 119 | } 120 | return -1, false 121 | } 122 | -------------------------------------------------------------------------------- /binary_search_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import "testing" 4 | 5 | func TestLowerBound(t *testing.T) { 6 | a := []int{1, 2, 4, 5, 5, 6} 7 | expectEq(t, LowerBound(a, 1), 0) 8 | expectEq(t, LowerBound(a, 5), 3) 9 | expectEq(t, LowerBound(a, 7), len(a)) 10 | } 11 | 12 | func TestLowerBoundFunc(t *testing.T) { 13 | a := []int{1, 2, 4, 5, 5, 6} 14 | expectEq(t, LowerBoundFunc(a, 1, Less[int]), 0) 15 | expectEq(t, LowerBoundFunc(a, 5, Less[int]), 3) 16 | expectEq(t, LowerBoundFunc(a, 7, Less[int]), len(a)) 17 | } 18 | 19 | func TestUpperBound(t *testing.T) { 20 | a := []int{1, 2, 4, 5, 5, 6} 21 | expectEq(t, UpperBound(a, 1), 1) 22 | expectEq(t, UpperBound(a, 5), 5) 23 | expectEq(t, UpperBound(a, 7), len(a)) 24 | } 25 | 26 | func TestUpperBoundFunc(t *testing.T) { 27 | a := []int{1, 2, 4, 5, 5, 6} 28 | expectEq(t, UpperBoundFunc(a, 1, Less[int]), 1) 29 | expectEq(t, UpperBoundFunc(a, 5, Less[int]), 5) 30 | expectEq(t, UpperBoundFunc(a, 7, Less[int]), len(a)) 31 | } 32 | 33 | func TestBinarySearch(t *testing.T) { 34 | a := []int{1, 2, 4, 5, 5, 6} 35 | 36 | i, ok := BinarySearch(a, 4) 37 | expectTrue(t, ok) 38 | expectEq(t, i, 2) 39 | 40 | i, ok = BinarySearch(a, 5) 41 | expectTrue(t, ok) 42 | expectEq(t, i, 3) 43 | 44 | i, ok = BinarySearch(a, 3) 45 | expectFalse(t, ok) 46 | } 47 | 48 | func TestBinarySearchFunc(t *testing.T) { 49 | a := []int{1, 2, 4, 5, 5, 6} 50 | 51 | i, ok := BinarySearchFunc(a, 4, Less[int]) 52 | expectTrue(t, ok) 53 | expectEq(t, i, 2) 54 | 55 | i, ok = BinarySearchFunc(a, 5, Less[int]) 56 | expectTrue(t, ok) 57 | expectEq(t, i, 3) 58 | 59 | i, ok = BinarySearchFunc(a, 3, Less[int]) 60 | expectFalse(t, ok) 61 | } 62 | -------------------------------------------------------------------------------- /builtin_set.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // BuiltinSet is an associative container that contains an unordered set of unique objects of type K. 8 | type BuiltinSet[K comparable] map[K]struct{} 9 | 10 | // SetOf creates a new BuiltinSet object with the initial content from ks. 11 | func SetOf[K comparable](ks ...K) BuiltinSet[K] { 12 | s := make(BuiltinSet[K]) 13 | s.InsertN(ks...) 14 | return s 15 | } 16 | 17 | // IsEmpty implements the Container interface. 18 | func (s BuiltinSet[K]) IsEmpty() bool { 19 | return len(s) == 0 20 | } 21 | 22 | // Len implements the Container interface. 23 | func (s BuiltinSet[K]) Len() int { 24 | return len(s) 25 | } 26 | 27 | // Clear implements the Container interface. 28 | func (s BuiltinSet[K]) Clear() { 29 | for k := range s { 30 | delete(s, k) 31 | } 32 | } 33 | 34 | // Has implements the Set interface. 35 | func (s BuiltinSet[K]) Has(k K) bool { 36 | _, ok := s[k] 37 | return ok 38 | } 39 | 40 | // Insert implements the Set interface. 41 | func (s BuiltinSet[K]) Insert(k K) bool { 42 | oldLen := len(s) 43 | s[k] = struct{}{} 44 | return len(s) > oldLen 45 | } 46 | 47 | // InsertN implements the Set interface. 48 | func (s BuiltinSet[K]) InsertN(ks ...K) int { 49 | oldLen := len(s) 50 | for _, key := range ks { 51 | s[key] = struct{}{} 52 | } 53 | return len(s) - oldLen 54 | } 55 | 56 | // Remove implements the Set interface. 57 | func (s BuiltinSet[K]) Remove(k K) bool { 58 | _, ok := s[k] 59 | delete(s, k) 60 | return ok 61 | } 62 | 63 | // Delete deletes an element from the set. 64 | // It returns nothing, so it's faster than Remove. 65 | func (s BuiltinSet[K]) Delete(k K) { 66 | delete(s, k) 67 | } 68 | 69 | // RemoveN implements the Set interface. 70 | func (s BuiltinSet[K]) RemoveN(ks ...K) int { 71 | oldLen := len(s) 72 | for _, k := range ks { 73 | delete(s, k) 74 | } 75 | return oldLen - len(s) 76 | } 77 | 78 | // Keys return a copy of all keys as a slice. 79 | func (s BuiltinSet[K]) Keys() []K { 80 | keys := make([]K, 0, len(s)) 81 | for k := range s { 82 | keys = append(keys, k) 83 | } 84 | return keys 85 | } 86 | 87 | // ForEach implements the Set interface. 88 | func (s BuiltinSet[K]) ForEach(cb func(k K)) { 89 | for k := range s { 90 | cb(k) 91 | } 92 | } 93 | 94 | // ForEachIf implements the Container interface. 95 | func (s BuiltinSet[K]) ForEachIf(cb func(k K) bool) { 96 | for k := range s { 97 | if !cb(k) { 98 | break 99 | } 100 | } 101 | } 102 | 103 | // String implements the fmt.Stringer interface. 104 | func (s BuiltinSet[K]) String() string { 105 | return fmt.Sprintf("BuiltinSet[%s]%v", nameOfType[K](), s.Keys()) 106 | } 107 | 108 | // Update adds all elements from other to set. set |= other. 109 | func (s BuiltinSet[K]) Update(other BuiltinSet[K]) { 110 | for k := range other { 111 | s[k] = struct{}{} 112 | } 113 | } 114 | 115 | // Union returns a new set with elements from the set and other. 116 | func (s BuiltinSet[K]) Union(other BuiltinSet[K]) BuiltinSet[K] { 117 | result := BuiltinSet[K]{} 118 | result.Update(s) 119 | result.Update(other) 120 | return result 121 | } 122 | 123 | func orderSet[K comparable](a, b BuiltinSet[K]) (small, large BuiltinSet[K]) { 124 | if a.Len() < b.Len() { 125 | return a, b 126 | } 127 | return b, a 128 | } 129 | 130 | // Intersection returns a new set with elements common to the set and other. 131 | func (s BuiltinSet[K]) Intersection(other BuiltinSet[K]) BuiltinSet[K] { 132 | result := BuiltinSet[K]{} 133 | small, large := orderSet(s, other) 134 | for k := range small { 135 | if large.Has(k) { 136 | result.Insert(k) 137 | } 138 | } 139 | return result 140 | } 141 | 142 | // Difference returns a new set with elements in the set that are not in other. 143 | func (s BuiltinSet[K]) Difference(other BuiltinSet[K]) BuiltinSet[K] { 144 | result := BuiltinSet[K]{} 145 | for k := range s { 146 | if !other.Has(k) { 147 | result.Insert(k) 148 | } 149 | } 150 | return result 151 | } 152 | 153 | // IsDisjointOf return True if the set has no elements in common with other. 154 | // Sets are disjoint if and only if their intersection is the empty set. 155 | func (s BuiltinSet[K]) IsDisjointOf(other BuiltinSet[K]) bool { 156 | small, large := orderSet(s, other) 157 | for k := range small { 158 | if large.Has(k) { 159 | return false 160 | } 161 | } 162 | return true 163 | } 164 | 165 | // IsSubsetOf tests whether every element in the set is in other. 166 | func (s BuiltinSet[K]) IsSubsetOf(other BuiltinSet[K]) bool { 167 | if s.Len() > other.Len() { 168 | return false 169 | } 170 | for k := range s { 171 | if !other.Has(k) { 172 | return false 173 | } 174 | } 175 | return true 176 | } 177 | 178 | // IsSupersetOf tests whether every element in other is in the set. 179 | func (s BuiltinSet[K]) IsSupersetOf(other BuiltinSet[K]) bool { 180 | return other.IsSubsetOf(s) 181 | } 182 | -------------------------------------------------------------------------------- /builtin_set_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func Test_BuiltinSet_Interface(t *testing.T) { 10 | s := make(BuiltinSet[int]) 11 | _ = Set[int](&s) 12 | } 13 | 14 | func Test_MakeBuiltinSet(t *testing.T) { 15 | s := make(BuiltinSet[string]) 16 | expectEq(t, s.Len(), 0) 17 | expectEq(t, s.IsEmpty(), true) 18 | } 19 | 20 | func Test_MakeBuiltinSet2(t *testing.T) { 21 | s := BuiltinSet[string]{} 22 | expectEq(t, s.Len(), 0) 23 | expectEq(t, s.IsEmpty(), true) 24 | } 25 | 26 | func Test_SetOf(t *testing.T) { 27 | s := SetOf("hello", "world") 28 | expectEq(t, s.Len(), 2) 29 | } 30 | 31 | func Test_BuiltinSet_IsEmpty(t *testing.T) { 32 | s := make(BuiltinSet[string]) 33 | expectEq(t, s.IsEmpty(), true) 34 | s.Insert("hello") 35 | expectEq(t, s.IsEmpty(), false) 36 | } 37 | 38 | func Test_BuiltinSet_Clear(t *testing.T) { 39 | s := SetOf("hello", "world") 40 | s.Clear() 41 | expectTrue(t, s.IsEmpty()) 42 | } 43 | 44 | func Test_BuiltinSet_String(t *testing.T) { 45 | s := SetOf("hello", "world") 46 | expectTrue(t, strings.HasPrefix(fmt.Sprintf("%v", s), "BuiltinSet[string]")) 47 | } 48 | 49 | func Test_BuiltinSet_Has(t *testing.T) { 50 | s := SetOf("hello", "world") 51 | expectTrue(t, s.Has("hello")) 52 | expectTrue(t, s.Has("world")) 53 | expectFalse(t, s.Has("!")) 54 | } 55 | 56 | func Test_BuiltinSet_Insert(t *testing.T) { 57 | s := make(BuiltinSet[string]) 58 | expectTrue(t, s.Insert("hello")) 59 | expectFalse(t, s.Insert("hello")) 60 | expectEq(t, s.Has("world"), false) 61 | expectTrue(t, s.Insert("world")) 62 | expectEq(t, s.Has("hello"), true) 63 | expectEq(t, s.Len(), 2) 64 | } 65 | 66 | func Test_BuiltinSet_InsertN(t *testing.T) { 67 | s := make(BuiltinSet[string]) 68 | expectEq(t, s.InsertN("hello", "world"), 2) 69 | expectEq(t, s.Len(), 2) 70 | } 71 | 72 | func Test_BuiltinSet_Remove(t *testing.T) { 73 | s := SetOf("hello", "world") 74 | expectTrue(t, s.Remove("hello")) 75 | expectEq(t, s.Len(), 1) 76 | expectFalse(t, s.Remove("hello")) 77 | expectEq(t, s.Len(), 1) 78 | expectTrue(t, s.Remove("world")) 79 | expectEq(t, s.Len(), 0) 80 | } 81 | 82 | func Test_BuiltinSet_Delete(t *testing.T) { 83 | s := SetOf("hello", "world") 84 | s.Delete("hello") 85 | expectEq(t, s.Len(), 1) 86 | s.Delete("hello") 87 | expectEq(t, s.Len(), 1) 88 | s.Delete("world") 89 | expectEq(t, s.Len(), 0) 90 | } 91 | 92 | func Test_BuiltinSet_RemoveN(t *testing.T) { 93 | s := SetOf("hello", "world") 94 | expectEq(t, s.RemoveN("hello", "world"), 2) 95 | expectFalse(t, s.Remove("world")) 96 | expectTrue(t, s.IsEmpty()) 97 | } 98 | 99 | func Test_BuiltinSet_Keys(t *testing.T) { 100 | s := SetOf("hello", "world") 101 | ks := s.Keys() 102 | expectEq(t, 2, len(ks)) 103 | } 104 | 105 | func Test_BuiltinSet_For(t *testing.T) { 106 | s := SetOf("hello", "world") 107 | for v := range s { 108 | expectTrue(t, v == "hello" || v == "world") 109 | } 110 | } 111 | 112 | func Test_BuiltinSet_ForEach(t *testing.T) { 113 | s := SetOf("hello", "world") 114 | c := 0 115 | s.ForEach(func(string) { 116 | c++ 117 | }) 118 | expectEq(t, c, 2) 119 | } 120 | 121 | func Test_BuiltinSet_ForEachIf(t *testing.T) { 122 | s := SetOf("hello", "world") 123 | c := 0 124 | s.ForEachIf(func(string) bool { 125 | c++ 126 | return false 127 | }) 128 | expectLt(t, c, 2) 129 | } 130 | 131 | func Test_BuiltinSet_Update(t *testing.T) { 132 | s := SetOf(1, 2, 3) 133 | s.Update(SetOf(3, 4)) 134 | expectEq(t, s.Len(), 4) 135 | expectTrue(t, s.Has(4)) 136 | } 137 | 138 | func Test_BuiltinSet_Union(t *testing.T) { 139 | s := SetOf(1, 2, 3) 140 | s2 := s.Union(SetOf(3, 4)) 141 | expectEq(t, s2.Len(), 4) 142 | expectTrue(t, s2.Has(4)) 143 | } 144 | 145 | func Test_BuiltinSet_Intersection(t *testing.T) { 146 | s := SetOf(1, 2, 3).Intersection(SetOf(3, 4)) 147 | expectEq(t, s.Len(), 1) 148 | expectTrue(t, s.Has(3)) 149 | s = SetOf(3, 4).Intersection(SetOf(1, 2, 3)) 150 | expectEq(t, s.Len(), 1) 151 | expectTrue(t, s.Has(3)) 152 | } 153 | 154 | func Test_BuiltinSet_Difference(t *testing.T) { 155 | s := SetOf(1, 2, 3).Difference(SetOf(3, 4)) 156 | expectEq(t, s.Len(), 2) 157 | expectTrue(t, s.Has(1)) 158 | expectTrue(t, s.Has(2)) 159 | s = SetOf(1, 2).Difference(SetOf(3, 4)) 160 | expectEq(t, s.Len(), 2) 161 | expectTrue(t, s.Has(1)) 162 | expectTrue(t, s.Has(2)) 163 | } 164 | 165 | func Test_BuiltinSet_IsDisjointOf(t *testing.T) { 166 | s1 := SetOf(1, 2, 3) 167 | s2 := SetOf(3, 4) 168 | expectFalse(t, s1.IsDisjointOf(s2)) 169 | expectTrue(t, s1.IsDisjointOf(SetOf(4, 5))) 170 | } 171 | 172 | func Test_BuiltinSet_IsSubsetOf(t *testing.T) { 173 | expectTrue(t, SetOf[int]().IsSubsetOf(SetOf[int]())) 174 | expectTrue(t, SetOf[int]().IsSubsetOf(SetOf(1))) 175 | expectTrue(t, SetOf(1, 2, 3).IsSubsetOf(SetOf(1, 2, 3))) 176 | expectTrue(t, SetOf(1, 2).IsSubsetOf(SetOf(1, 2, 3))) 177 | expectFalse(t, SetOf(1, 2, 3).IsSubsetOf(SetOf(1, 2))) 178 | expectFalse(t, SetOf(1, 2).IsSubsetOf(SetOf(2, 3))) 179 | } 180 | 181 | func Test_BuiltinSet_IsSupersetOf(t *testing.T) { 182 | expectTrue(t, SetOf[int]().IsSupersetOf(SetOf[int]())) 183 | expectTrue(t, SetOf(1).IsSupersetOf(SetOf[int]())) 184 | expectTrue(t, SetOf(1, 2, 3).IsSupersetOf(SetOf(1, 2, 3))) 185 | expectTrue(t, SetOf(1, 2, 3).IsSupersetOf(SetOf(1, 2))) 186 | expectFalse(t, SetOf(1, 2).IsSupersetOf(SetOf(1, 2, 3))) 187 | expectFalse(t, SetOf(1, 2).IsSupersetOf(SetOf(2, 3))) 188 | } 189 | -------------------------------------------------------------------------------- /check.bat: -------------------------------------------------------------------------------- 1 | go build 2 | 3 | go test ./... -coverprofile=coverage.out 4 | 5 | golint 6 | 7 | gosec . 8 | 9 | go tool cover -html=coverage.out 10 | -------------------------------------------------------------------------------- /check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | go build 6 | 7 | go test ./... -coverprofile=coverage.out 8 | 9 | golint 10 | 11 | gosec . 12 | 13 | go tool cover -html=coverage.out 14 | -------------------------------------------------------------------------------- /compare.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | // Equal returns whether two slices are equal. 4 | // Return true if they are the same length and all elements are equal. 5 | // 6 | // Complexity: O(min(len(a), len(b))). 7 | func Equal[T comparable](a, b []T) bool { 8 | if len(a) != len(b) { 9 | return false 10 | } 11 | for i := range a { 12 | if a[i] != b[i] { 13 | return false 14 | } 15 | } 16 | return true 17 | } 18 | 19 | // Compare compares each elements in a and b. 20 | // 21 | // return 0 if they are equals, 22 | // return 1 if a > b, 23 | // return -1 if a < b. 24 | // 25 | // Complexity: O(min(len(a), len(b))). 26 | func Compare[E Ordered](a, b []E) int { 27 | bl := len(b) 28 | for i, v1 := range a { 29 | if i >= bl { 30 | return 1 31 | } 32 | v2 := b[i] 33 | switch { 34 | case v1 < v2: 35 | return -1 36 | case v1 > v2: 37 | return 1 38 | } 39 | } 40 | if len(a) < bl { 41 | return -1 42 | } 43 | return 0 44 | } 45 | -------------------------------------------------------------------------------- /compare_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import "testing" 4 | 5 | func Test_Equal(t *testing.T) { 6 | expectTrue(t, Equal([]int{}, []int{})) 7 | expectTrue(t, Equal([]int{1, 2, 3}, []int{1, 2, 3})) 8 | expectFalse(t, Equal([]int{1, 2}, []int{1, 2, 3})) 9 | expectFalse(t, Equal([]int{1, 2, 2}, []int{1, 2, 3})) 10 | } 11 | 12 | func Test_Compare(t *testing.T) { 13 | expectEq(t, Compare([]int{}, []int{}), 0) 14 | expectEq(t, Compare([]int{1, 2, 3}, []int{1, 2, 3}), 0) 15 | expectEq(t, Compare([]int{1, 2, 2}, []int{1, 2, 3}), -1) 16 | expectEq(t, Compare([]int{1, 2, 4}, []int{1, 2, 3}), 1) 17 | expectEq(t, Compare([]int{1, 2}, []int{1, 2, 3}), -1) 18 | expectEq(t, Compare([]int{1, 2, 3}, []int{1, 2}), 1) 19 | } 20 | -------------------------------------------------------------------------------- /compute.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | // SumAs summarize all elements in a. 4 | // returns the result as type R, this is useful when T is too small to hold the result. 5 | // Complexity: O(len(a)). 6 | func SumAs[R, T Numeric](a []T) R { 7 | switch zero := T(0); any(zero).(type) { 8 | case int8, int16, int32, int, int64: 9 | var total int64 10 | for _, v := range a { 11 | total += int64(v) 12 | } 13 | return R(total) 14 | case uint8, uint16, uint32, uint, uint64, uintptr: 15 | var total uint64 16 | for _, v := range a { 17 | total += uint64(v) 18 | } 19 | return R(total) 20 | default: 21 | var total float64 22 | for _, v := range a { 23 | total += float64(v) 24 | } 25 | return R(total) 26 | } 27 | } 28 | 29 | // Sum summarize all elements in a. 30 | // returns the result as type R, you should use SumAs if T can't hold the result. 31 | // Complexity: O(len(a)). 32 | func Sum[T Numeric](a []T) T { 33 | return SumAs[T](a) 34 | } 35 | 36 | // AverageAs returns the average value of a as type R. 37 | func AverageAs[R, T Numeric](a []T) R { 38 | return SumAs[R](a) / R(len(a)) 39 | } 40 | 41 | // Average returns the average value of a. 42 | func Average[T Numeric](a []T) T { 43 | var zero T // NOTE: convert 0 to interface have no malloc 44 | switch any(zero).(type) { 45 | case int, int8, uint8, int16, uint16, int32, uint32: 46 | return T(AverageAs[int64](a)) 47 | case uint64: 48 | return T(AverageAs[uint64](a)) 49 | case float32, float64: 50 | return T(AverageAs[float64](a)) 51 | } 52 | // int64, uint64, uintptr ... 53 | return AverageAs[T](a) 54 | } 55 | 56 | // Count returns the number of elements in the slice equals to x. 57 | // 58 | // Complexity: O(len(a)). 59 | func Count[T comparable](a []T, x T) int { 60 | c := 0 61 | for _, v := range a { 62 | if v == x { 63 | c++ 64 | } 65 | } 66 | return c 67 | } 68 | 69 | // CountIf returns the number of elements in the slice which pred returns true. 70 | // 71 | // Complexity: O(len(a)). 72 | func CountIf[T comparable](a []T, pred func(T) bool) int { 73 | c := 0 74 | for _, v := range a { 75 | if pred(v) { 76 | c++ 77 | } 78 | } 79 | return c 80 | } 81 | -------------------------------------------------------------------------------- /compute_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_SumAs(t *testing.T) { 8 | t.Run("sum uint8 to int", func(t *testing.T) { 9 | a := Range[uint8](1, 101) 10 | expectEq(t, SumAs[int](a), 5050) 11 | }) 12 | 13 | t.Run("sum int to uint8", func(t *testing.T) { 14 | a := Range[int](1, 101) 15 | expectEq(t, SumAs[uint8](a), uint8(5050%256)) 16 | }) 17 | 18 | t.Run("sum int64 to float64", func(t *testing.T) { 19 | a := Range[int64](1, 101) 20 | expectEq(t, SumAs[float64](a), 5050.) 21 | }) 22 | 23 | t.Run("sum float64 to int64", func(t *testing.T) { 24 | a := Range[float64](1.1, 101.1) 25 | expectEq(t, SumAs[int](a), 101.2*50) // 5060 26 | }) 27 | } 28 | 29 | func Test_Sum(t *testing.T) { 30 | a := Range(1, 101) 31 | expectEq(t, Sum(a), 5050) 32 | } 33 | 34 | func Test_Average(t *testing.T) { 35 | a := Range(1, 101) 36 | expectEq(t, Average(a), 50) 37 | } 38 | 39 | func Test_AverageAs(t *testing.T) { 40 | a := []int{1, 0} 41 | expectEq(t, AverageAs[float64](a), 0.5) 42 | } 43 | 44 | func Test_Average_U64(t *testing.T) { 45 | a := Range[uint64](0, 101) 46 | expectEq(t, Average(a), 50) 47 | } 48 | 49 | func Test_Average_Float(t *testing.T) { 50 | a := Range(0.0, 101.0) 51 | expectEq(t, Average(a), 50.0) 52 | } 53 | 54 | func Test_Average_SmallType(t *testing.T) { 55 | a := Range[uint8](0, 101) 56 | expectEq(t, Average(a), 50) 57 | } 58 | 59 | func Test_Average_UintPtr(t *testing.T) { 60 | a := Range[uintptr](0, 101) 61 | expectEq(t, Average(a), 50) 62 | } 63 | 64 | func Test_Average_Signed(t *testing.T) { 65 | a := []int{-2, 1, -1, 2, 1, -1, 0} 66 | expectEq(t, Average(a), 0) 67 | } 68 | 69 | func Test_Count(t *testing.T) { 70 | a := []int{1, 2, 3, 4, 3} 71 | expectEq(t, Count(a, 3), 2) 72 | expectEq(t, CountIf(pos, isNegative), 0) 73 | expectEq(t, CountIf(neg, isNegative), 5) 74 | expectEq(t, CountIf(mix, isNegative), 2) 75 | } 76 | -------------------------------------------------------------------------------- /container.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | // Container is a holder object that stores a collection of other objects. 4 | type Container interface { 5 | IsEmpty() bool // IsEmpty checks if the container has no elements. 6 | Len() int // Len returns the number of elements in the container. 7 | Clear() // Clear erases all elements from the container. After this call, Len() returns zero. 8 | } 9 | 10 | // Map is a associative container that contains key-value pairs with unique keys. 11 | type Map[K any, V any] interface { 12 | Container 13 | Has(K) bool // Checks whether the container contains element with specific key. 14 | Find(K) *V // Finds element with specific key. 15 | Insert(K, V) // Inserts a key-value pair in to the container or replace existing value. 16 | Remove(K) bool // Remove element with specific key. 17 | ForEach(func(K, V)) // Iterate the container. 18 | ForEachIf(func(K, V) bool) // Iterate the container, stops when the callback returns false. 19 | ForEachMutable(func(K, *V)) // Iterate the container, *V is mutable. 20 | ForEachMutableIf(func(K, *V) bool) // Iterate the container, *V is mutable, stops when the callback returns false. 21 | } 22 | 23 | // Set is a containers that store unique elements. 24 | type Set[K any] interface { 25 | Container 26 | Has(K) bool // Checks whether the container contains element with specific key. 27 | Insert(K) bool // Inserts a element in to the container or replace existing value. 28 | InsertN(...K) int // Inserts multiple elements in to the container or replace existing value. 29 | Remove(K) bool // Remove specific element, return true if element was in the container. 30 | RemoveN(...K) int // Remove multiple elements, return the number of removed elements. 31 | ForEach(func(K)) // Iterate the container. 32 | ForEachIf(func(K) bool) // Iterate the container, stops when the callback returns false. 33 | } 34 | 35 | // SortedMap is a Map that provides a total ordering on its keys. 36 | type SortedMap[K any, V any] interface { 37 | Map[K, V] 38 | // LowerBound returns an iterator to the first element in the container that 39 | // does not satisfy element.key < value (i.e. greater or equal to), 40 | // or a end iterator if no such element is found. 41 | LowerBound(K) MutableMapIterator[K, V] 42 | 43 | // UpperBound returns an iterator to the first element in the container that 44 | // does not satisfy value < element.key (i.e. strictly greater), 45 | // or a end iterator if no such element is found. 46 | UpperBound(K) MutableMapIterator[K, V] 47 | 48 | // FindRange returns an iterator in range [first, last) (last is not included). 49 | FindRange(K, K) MutableMapIterator[K, V] 50 | } 51 | 52 | // SortedSet is a Set that provides a total ordering on its elements. 53 | type SortedSet[K any] interface { 54 | Set[K] 55 | 56 | // LowerBound returns an iterator to the first element in the container that 57 | // does not satisfy element < value (i.e. greater or equal to), 58 | // or a end iterator if no such element is found. 59 | LowerBound(K) Iterator[K] 60 | 61 | // UpperBound returns an iterator to the first element in the container that 62 | // does not satisfy value < element (i.e. strictly greater), 63 | // or a end iterator if no such element is found. 64 | UpperBound(K) Iterator[K] 65 | 66 | // FindRange returns an iterator in range [first, last) (last is not included). 67 | FindRange(K, K) Iterator[K] 68 | } 69 | 70 | // Queue is a container that can add elements to one end and remove elements from the other end. 71 | type Queue[T any] interface { 72 | Container 73 | Front() // Front returns the first element in the container. 74 | Back() // Back returns the last element in the container. 75 | Push(T) // Push pushes an element at the back of the container. 76 | Pop() T // Pop popups a front from the back of the container. 77 | TryPop() (T, bool) // TryPop tries to popup a element from the front of the container. 78 | } 79 | 80 | // Deque is a container that can add and remove elements from both ends. 81 | type Deque[T any] interface { 82 | Container 83 | Front() T // Front returns the first element in the container. 84 | Back() T // Back returns the last element in the container. 85 | PushFront(T) // PushBack pushes an element at the front of the container. 86 | PushBack(T) // PushBack pushes an element at the back of the container. 87 | PopFront() T // PopBack popups a front from the back of the container. 88 | PopBack() T // PopBack popups a element from the back of the container. 89 | TryPopFront() (T, bool) // TryPopFront tries to popup a element from the front of the container. 90 | TryPopBack() (T, bool) // TryPopBack tries to popup a element from the back of the container. 91 | } 92 | -------------------------------------------------------------------------------- /container_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | -------------------------------------------------------------------------------- /dlist.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import "fmt" 4 | 5 | // DList is a doubly linked list. 6 | type DList[T any] struct { 7 | head *dListNode[T] 8 | length int 9 | } 10 | 11 | type dListNode[T any] struct { 12 | prev, next *dListNode[T] 13 | value T 14 | } 15 | 16 | // DListOf make a new DList from a serial of values. 17 | func DListOf[T any](vs ...T) DList[T] { 18 | l := DList[T]{} 19 | for _, v := range vs { 20 | l.PushBack(v) 21 | } 22 | return l 23 | } 24 | 25 | // Clear cleanup the list. 26 | func (l *DList[T]) Clear() { 27 | if l.head != nil { 28 | l.head.prev = l.head 29 | l.head.next = l.head 30 | } 31 | l.length = 0 32 | } 33 | 34 | // Len return the length of the list. 35 | func (l *DList[T]) Len() int { 36 | return l.length 37 | } 38 | 39 | // IsEmpty return whether the list is empty. 40 | func (l *DList[T]) IsEmpty() bool { 41 | return l.length == 0 42 | } 43 | 44 | // String convert the list to string. 45 | func (l *DList[T]) String() string { 46 | return fmt.Sprintf("DList[%v]", nameOfType[T]()) 47 | } 48 | 49 | type dlistIterator[T any] struct { 50 | dl *DList[T] 51 | node *dListNode[T] 52 | } 53 | 54 | func (it *dlistIterator[T]) IsNotEnd() bool { 55 | return it.node != it.dl.head 56 | } 57 | 58 | func (it *dlistIterator[T]) MoveToNext() { 59 | it.node = it.node.next 60 | } 61 | 62 | func (it *dlistIterator[T]) Value() T { 63 | return it.node.value 64 | } 65 | 66 | func (it *dlistIterator[T]) Pointer() *T { 67 | return &it.node.value 68 | } 69 | 70 | // Iterate returns an iterator to the first element in the list. 71 | func (l *DList[T]) Iterate() MutableIterator[T] { 72 | node := l.head 73 | if node != nil { 74 | node = node.next 75 | } 76 | return &dlistIterator[T]{l, node} 77 | } 78 | 79 | // Front returns the first element in the container. 80 | func (l *DList[T]) Front() T { 81 | if l.IsEmpty() { 82 | panic("!IsEmpty") 83 | } 84 | return l.head.next.value 85 | } 86 | 87 | // Back returns the last element in the container. 88 | func (l *DList[T]) Back() T { 89 | if l.IsEmpty() { 90 | panic("!IsEmpty") 91 | } 92 | return l.head.prev.value 93 | } 94 | 95 | // PushFront pushes an element at the front of the list. 96 | func (l *DList[T]) PushFront(val T) { 97 | l.ensureHead() 98 | n := dListNode[T]{l.head, l.head.next, val} 99 | l.head.next.prev = &n 100 | l.head.next = &n 101 | l.length++ 102 | } 103 | 104 | // PushBack pushes an element at the back of the list. 105 | func (l *DList[T]) PushBack(val T) { 106 | l.ensureHead() 107 | n := dListNode[T]{l.head.prev, l.head, val} 108 | l.head.prev.next = &n 109 | l.head.prev = &n 110 | l.length++ 111 | } 112 | 113 | // PopFront popups an element from the front of the list. 114 | func (l *DList[T]) PopFront() T { 115 | r, ok := l.TryPopFront() 116 | if !ok { 117 | panic("DList.PopFront: empty list") 118 | } 119 | return r 120 | } 121 | 122 | // PopBack popups an element from the back of the list. 123 | func (l *DList[T]) PopBack() T { 124 | r, ok := l.TryPopBack() 125 | if !ok { 126 | panic("DList.PopBack: empty list") 127 | } 128 | return r 129 | } 130 | 131 | // TryPopFront tries to pop up an element from the front of the list. 132 | func (l *DList[T]) TryPopFront() (T, bool) { 133 | var val T 134 | if l.IsEmpty() { 135 | return val, false 136 | } 137 | node := l.head.next 138 | val = node.value 139 | l.head.next = node.next 140 | l.head.next.prev = l.head 141 | node.prev = nil 142 | node.next = nil 143 | l.length-- 144 | return val, true 145 | } 146 | 147 | // TryPopBack tries to pop up an element from the back of the list. 148 | func (l *DList[T]) TryPopBack() (T, bool) { 149 | var val T 150 | if l.IsEmpty() { 151 | return val, false 152 | } 153 | node := l.head.prev 154 | val = node.value 155 | l.head.prev = l.head.prev.prev 156 | l.head.prev.next = l.head 157 | node.prev = nil 158 | node.next = nil 159 | l.length-- 160 | return val, true 161 | } 162 | 163 | // ForEach iterate the list, apply each element to the cb callback function. 164 | func (l *DList[T]) ForEach(cb func(val T)) { 165 | if l.IsEmpty() { 166 | return 167 | } 168 | for n := l.head.next; n != l.head; n = n.next { 169 | cb(n.value) 170 | } 171 | } 172 | 173 | // ForEachIf iterate the list, apply each element to the cb callback function, 174 | // stop if cb returns false. 175 | func (l *DList[T]) ForEachIf(cb func(val T) bool) { 176 | if l.IsEmpty() { 177 | return 178 | } 179 | for n := l.head.next; n != l.head; n = n.next { 180 | if !cb(n.value) { 181 | break 182 | } 183 | } 184 | } 185 | 186 | // ForEachMutable iterate the list, apply pointer of each element to the cb callback function. 187 | func (l *DList[T]) ForEachMutable(cb func(val *T)) { 188 | if l.IsEmpty() { 189 | return 190 | } 191 | for n := l.head.next; n != l.head; n = n.next { 192 | cb(&n.value) 193 | } 194 | } 195 | 196 | // ForEachMutableIf iterate the list, apply pointer of each element to the cb callback function, 197 | // stop if cb returns false. 198 | func (l *DList[T]) ForEachMutableIf(cb func(val *T) bool) { 199 | if l.IsEmpty() { 200 | return 201 | } 202 | for n := l.head.next; n != l.head; n = n.next { 203 | if !cb(&n.value) { 204 | break 205 | } 206 | } 207 | } 208 | 209 | // ensureHead ensure head is valid. 210 | func (l *DList[T]) ensureHead() { 211 | if l.head == nil { 212 | l.head = &dListNode[T]{} 213 | l.head.prev = l.head 214 | l.head.next = l.head 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /dlist_queue.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // DListQueue is a FIFO container 8 | type DListQueue[T any] struct { 9 | list DList[T] 10 | } 11 | 12 | // NewDListQueue create a new Queue object. 13 | func NewDListQueue[T any]() *DListQueue[T] { 14 | q := DListQueue[T]{} 15 | return &q 16 | } 17 | 18 | // Len implements the Container interface. 19 | func (q *DListQueue[T]) Len() int { 20 | return q.list.Len() 21 | } 22 | 23 | // IsEmpty implements the Container interface. 24 | func (q *DListQueue[T]) IsEmpty() bool { 25 | return q.list.IsEmpty() 26 | } 27 | 28 | // Clear implements the Container interface. 29 | func (q *DListQueue[T]) Clear() { 30 | q.list.Clear() 31 | } 32 | 33 | // Len implements the fmt.Stringer interface. 34 | func (q *DListQueue[T]) String() string { 35 | return fmt.Sprintf("Queue[%v]", nameOfType[T]()) 36 | } 37 | 38 | // Front returns the first element in the container. 39 | func (q *DListQueue[T]) Front() T { 40 | return q.list.Front() 41 | } 42 | 43 | // Back returns the last element in the container. 44 | func (q *DListQueue[T]) Back() T { 45 | return q.list.Back() 46 | } 47 | 48 | // PushFront pushed an element to the front of the queue. 49 | func (q *DListQueue[T]) PushFront(val T) { 50 | q.list.PushFront(val) 51 | } 52 | 53 | // PushBack pushed an element to the back of the queue. 54 | func (q *DListQueue[T]) PushBack(val T) { 55 | q.list.PushBack(val) 56 | } 57 | 58 | // PopFront popups an element from the front of the queue. 59 | func (q *DListQueue[T]) PopFront() T { 60 | return q.list.PopFront() 61 | } 62 | 63 | // PopBack popups an element from the back of the queue. 64 | func (q *DListQueue[T]) PopBack() T { 65 | return q.list.PopBack() 66 | } 67 | 68 | // TryPopFront tries popuping an element from the front of the queue. 69 | func (q *DListQueue[T]) TryPopFront() (T, bool) { 70 | return q.list.TryPopFront() 71 | } 72 | 73 | // TryPopBack tries popuping an element from the back of the queue. 74 | func (q *DListQueue[T]) TryPopBack() (T, bool) { 75 | return q.list.TryPopBack() 76 | } 77 | -------------------------------------------------------------------------------- /dlist_queue_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_Queue_Interface(t *testing.T) { 8 | q := NewDListQueue[int]() 9 | _ = Deque[int](q) 10 | } 11 | 12 | func Test_Queue_New(t *testing.T) { 13 | q := NewDListQueue[int]() 14 | expectTrue(t, q.IsEmpty()) 15 | expectEq(t, q.Len(), 0) 16 | } 17 | 18 | func Test_Queue_Clear(t *testing.T) { 19 | q := NewDListQueue[int]() 20 | q.PushBack(1) 21 | q.Clear() 22 | expectTrue(t, q.IsEmpty()) 23 | expectEq(t, q.Len(), 0) 24 | } 25 | 26 | func Test_Queue_String(t *testing.T) { 27 | q := NewDListQueue[int]() 28 | expectEq(t, q.String(), "Queue[int]") 29 | } 30 | 31 | func Test_Queue_Front_Back(t *testing.T) { 32 | q := NewDListQueue[int]() 33 | expectPanic(t, func() { q.Front() }) 34 | expectPanic(t, func() { q.Back() }) 35 | q.PushBack(1) 36 | q.PushBack(2) 37 | expectEq(t, q.Front(), 1) 38 | expectEq(t, q.Back(), 2) 39 | } 40 | 41 | func Test_Queue_PushFront(t *testing.T) { 42 | q := NewDListQueue[int]() 43 | q.PushFront(1) 44 | expectFalse(t, q.IsEmpty()) 45 | expectEq(t, q.Len(), 1) 46 | } 47 | 48 | func Test_Queue_PushBack(t *testing.T) { 49 | q := NewDListQueue[int]() 50 | q.PushBack(1) 51 | expectFalse(t, q.IsEmpty()) 52 | expectEq(t, q.Len(), 1) 53 | } 54 | 55 | func Test_Queue_TryPopFront(t *testing.T) { 56 | q := NewDListQueue[int]() 57 | _, ok := q.TryPopFront() 58 | expectFalse(t, ok) 59 | } 60 | func Test_Queue_TryPopBack(t *testing.T) { 61 | q := NewDListQueue[int]() 62 | _, ok := q.TryPopBack() 63 | expectFalse(t, ok) 64 | } 65 | 66 | func Test_Queue_PushFront_PopFront(t *testing.T) { 67 | q := NewDListQueue[int]() 68 | q.PushFront(1) 69 | q.PushFront(2) 70 | expectEq(t, q.PopFront(), 2) 71 | expectEq(t, q.PopFront(), 1) 72 | } 73 | 74 | func Test_Queue_PushFront_PopBack(t *testing.T) { 75 | q := NewDListQueue[int]() 76 | q.PushFront(1) 77 | expectEq(t, q.PopBack(), 1) 78 | } 79 | 80 | func Test_Queue_PushBack_PopFront(t *testing.T) { 81 | q := NewDListQueue[int]() 82 | q.PushBack(1) 83 | expectEq(t, q.PopFront(), 1) 84 | } 85 | 86 | func Test_Queue_PushBack_PopBack(t *testing.T) { 87 | q := NewDListQueue[int]() 88 | q.PushBack(1) 89 | expectEq(t, q.PopBack(), 1) 90 | } 91 | -------------------------------------------------------------------------------- /dlist_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_Dlist_Interface(t *testing.T) { 8 | _ = Container(&DList[int]{}) 9 | } 10 | 11 | func Test_DList_New(t *testing.T) { 12 | l := DList[int]{} 13 | expectTrue(t, l.IsEmpty()) 14 | expectEq(t, l.Len(), 0) 15 | } 16 | 17 | func Test_DListOf(t *testing.T) { 18 | l := DListOf(1, 2, 3) 19 | expectFalse(t, l.IsEmpty()) 20 | expectEq(t, l.Len(), 3) 21 | } 22 | 23 | func Test_DList_String(t *testing.T) { 24 | l := DList[int]{} 25 | expectEq(t, l.String(), "DList[int]") 26 | } 27 | 28 | func Test_DList_Iterate(t *testing.T) { 29 | l := DListOf(1, 2, 3) 30 | i := 1 31 | for it := l.Iterate(); it.IsNotEnd(); it.MoveToNext() { 32 | expectEq(t, it.Value(), i) 33 | expectEq(t, *it.Pointer(), i) 34 | i++ 35 | } 36 | expectEq(t, i, 4) 37 | } 38 | 39 | func Test_DList_Iterate_Empty(t *testing.T) { 40 | l := DList[int]{} 41 | i := 0 42 | for it := l.Iterate(); it.IsNotEnd(); it.MoveToNext() { 43 | i++ 44 | } 45 | expectEq(t, i, 0) 46 | } 47 | 48 | func Test_DList_FrontBack(t *testing.T) { 49 | l := DListOf(1, 2, 3) 50 | expectEq(t, l.Front(), 1) 51 | expectEq(t, l.Back(), 3) 52 | } 53 | 54 | func Test_DList_PushFront(t *testing.T) { 55 | l := DList[int]{} 56 | l.PushFront(1) 57 | expectFalse(t, l.IsEmpty()) 58 | expectEq(t, l.Len(), 1) 59 | } 60 | 61 | func Test_DList_PushBack(t *testing.T) { 62 | l := DList[int]{} 63 | l.PushBack(1) 64 | expectFalse(t, l.IsEmpty()) 65 | expectEq(t, l.Len(), 1) 66 | } 67 | 68 | func Test_DList_PopFront(t *testing.T) { 69 | l := DListOf(1, 2, 3, 4) 70 | expectEq(t, l.PopFront(), 1) 71 | expectEq(t, l.PopFront(), 2) 72 | 73 | n, ok := l.TryPopFront() 74 | expectEq(t, n, 3) 75 | expectTrue(t, ok) 76 | 77 | n, ok = l.TryPopBack() 78 | expectEq(t, n, 4) 79 | expectTrue(t, ok) 80 | 81 | n, ok = l.TryPopFront() 82 | expectFalse(t, ok) 83 | expectPanic(t, func() { l.PopFront() }) 84 | } 85 | 86 | func Test_DList_PopBack(t *testing.T) { 87 | l := DListOf(1, 2, 3, 4) 88 | expectEq(t, l.PopBack(), 4) 89 | expectEq(t, l.PopBack(), 3) 90 | 91 | n, ok := l.TryPopBack() 92 | expectTrue(t, ok) 93 | expectEq(t, n, 2) 94 | 95 | n, ok = l.TryPopFront() 96 | expectTrue(t, ok) 97 | expectEq(t, n, 1) 98 | 99 | n, ok = l.TryPopBack() 100 | expectFalse(t, ok) 101 | expectPanic(t, func() { l.PopBack() }) 102 | } 103 | 104 | func Test_DList_PushBack_PopFront(t *testing.T) { 105 | l := DList[int]{} 106 | l.PushBack(1) 107 | l.PushBack(2) 108 | 109 | v := l.PopFront() 110 | expectEq(t, v, 1) 111 | expectEq(t, l.PopFront(), 2) 112 | } 113 | 114 | func Test_DList_PushBack_PopBack(t *testing.T) { 115 | l := DList[int]{} 116 | l.PushBack(1) 117 | v := l.PopBack() 118 | expectEq(t, v, 1) 119 | } 120 | 121 | func Test_DList_PushFront_PopBack(t *testing.T) { 122 | l := DList[int]{} 123 | l.PushFront(1) 124 | v := l.PopBack() 125 | expectEq(t, v, 1) 126 | } 127 | 128 | func Test_DList_PushFront_PopFront(t *testing.T) { 129 | l := DList[int]{} 130 | l.PushFront(1) 131 | v := l.PopFront() 132 | expectEq(t, v, 1) 133 | } 134 | 135 | func Test_DList_ForEach(t *testing.T) { 136 | a := []int{1, 2, 3} 137 | l := DListOf(a...) 138 | var b []int 139 | l.ForEach(func(n int) { 140 | b = append(b, n) 141 | }) 142 | expectEq(t, len(b), 3) 143 | expectTrue(t, Equal(a, b)) 144 | } 145 | 146 | func Test_DList_ForEachIf(t *testing.T) { 147 | l := DListOf(1, 2, 3) 148 | c := 0 149 | l.ForEachIf(func(n int) bool { 150 | c = n 151 | return n != 2 152 | }) 153 | expectEq(t, c, 2) 154 | } 155 | 156 | func Test_DList_ForEachMutable(t *testing.T) { 157 | a := []int{1, 2, 3} 158 | l := DListOf(a...) 159 | l.ForEachMutable(func(n *int) { 160 | *n = -*n 161 | }) 162 | var b []int 163 | l.ForEach(func(n int) { 164 | b = append(b, n) 165 | }) 166 | expectEq(t, len(b), 3) 167 | for i := range b { 168 | expectEq(t, a[i], -b[i]) 169 | } 170 | } 171 | 172 | func Test_DList_ForEachMutableIf(t *testing.T) { 173 | l := DListOf(1, 2, 3) 174 | c := 0 175 | l.ForEachMutableIf(func(n *int) bool { 176 | c = *n 177 | return *n != 2 178 | }) 179 | expectEq(t, c, 2) 180 | } 181 | 182 | func Test_DList_ForEach_EmptyOK(t *testing.T) { 183 | l := DList[int]{} 184 | l.ForEach(func(n int) {}) 185 | l.ForEachIf(func(n int) bool { return true }) 186 | l.ForEachMutable(func(n *int) {}) 187 | l.ForEachMutableIf(func(n *int) bool { return true }) 188 | } 189 | 190 | func Benchmark_DList_Iterate(b *testing.B) { 191 | l := DListOf(Range(1, 10000)...) 192 | b.Run("Iterator", func(b *testing.B) { 193 | sum := 0 194 | for i := 0; i < b.N; i++ { 195 | for it := l.Iterate(); it.IsNotEnd(); it.MoveToNext() { 196 | sum += it.Value() 197 | } 198 | } 199 | }) 200 | b.Run("ForEach", func(b *testing.B) { 201 | sum := 0 202 | for i := 0; i < b.N; i++ { 203 | l.ForEach(func(val int) { sum += val }) 204 | } 205 | }) 206 | } 207 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Package stl4go is a generic container and algorithm library for go. 2 | package stl4go 3 | -------------------------------------------------------------------------------- /functor.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | // Less wraps the '<' operator for ordered types. 4 | func Less[T Ordered](a, b T) bool { 5 | return a < b 6 | } 7 | 8 | // Greater wraps the '>' operator for ordered types. 9 | func Greater[T Ordered](a, b T) bool { 10 | return a > b 11 | } 12 | 13 | // OrderedCompare provide default CompareFn for ordered types. 14 | func OrderedCompare[T Ordered](a, b T) int { 15 | if a < b { 16 | return -1 17 | } 18 | if a > b { 19 | return 1 20 | } 21 | return 0 22 | } 23 | -------------------------------------------------------------------------------- /generate.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | // Range make a []T filled with values in the `[first, last)` sequence. 4 | // NOTE: the last is not included in the result. 5 | // 6 | // Complexity: O(last-first). 7 | func Range[T Numeric](first, last T) []T { 8 | a := make([]T, 0, int(last-first)) 9 | for v := first; v < last; v++ { 10 | a = append(a, v) 11 | } 12 | return a 13 | } 14 | 15 | // Generate fill each element of `a`` with `gen()`. 16 | // 17 | // Complexity: O(len(a)). 18 | func Generate[T any](a []T, gen func() T) { 19 | for i := range a { 20 | a[i] = gen() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /generate_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import "testing" 4 | 5 | func Test_Range(t *testing.T) { 6 | a := Range(0, 100) 7 | expectEq(t, len(a), 100) 8 | expectEq(t, a[0], 0) 9 | expectEq(t, a[99], 99) 10 | } 11 | 12 | func Test_Generate(t *testing.T) { 13 | a := make([]int, 100) 14 | i := -1 15 | Generate(a, func() int { i++; return i }) 16 | for i, v := range a { 17 | expectEq(t, v, i) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/chen3feng/stl4go 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen3feng/stl4go/275098602fcfaa14be28f238236a617bfdfb1765/go.sum -------------------------------------------------------------------------------- /heap.go: -------------------------------------------------------------------------------- 1 | // Fast generic heap algorithms, faster than [container/heap] 2 | 3 | package stl4go 4 | 5 | // MakeMinHeap build a min-heap on slice array. 6 | // 7 | // Complexity: O(len(array)) 8 | func MakeMinHeap[T Ordered](array []T) { 9 | // heapify 10 | n := len(array) 11 | for i := n/2 - 1; i >= 0; i-- { 12 | heapDown(array, i, n) 13 | } 14 | } 15 | 16 | // IsMinHeap checks whether the elements in slice array are a min heap. 17 | // 18 | // Complexity: O(len(array)). 19 | func IsMinHeap[T Ordered](array []T) bool { 20 | parent := 0 21 | for child := 1; child < len(array); child++ { 22 | if array[parent] > array[child] { 23 | return false 24 | } 25 | 26 | if (child & 1) == 0 { 27 | parent++ 28 | } 29 | } 30 | return true 31 | } 32 | 33 | // PushMinHeap pushes a element v into the min heap. 34 | // 35 | // Complexity: O(log(len(*heap))). 36 | func PushMinHeap[T Ordered](heap *[]T, v T) { 37 | *heap = append(*heap, v) 38 | heapUp(*heap, len(*heap)-1) 39 | } 40 | 41 | // PopMinHeap removes and returns the minimum element from the heap. 42 | // 43 | // Complexity: O(log n) where n = len(*heap). 44 | func PopMinHeap[T Ordered](heap *[]T) T { 45 | h := *heap 46 | n := len(h) - 1 47 | heapSwap(h, 0, n) 48 | heapDown(h, 0, n) 49 | *heap = h[0:n] 50 | return h[n] 51 | } 52 | 53 | // RemoveMinHeap removes and returns the element at index i from the min heap. 54 | // 55 | // Complexity: is O(log(n)) where n = len(*heap). 56 | func RemoveMinHeap[T Ordered](heap *[]T, i int) T { 57 | h := *heap 58 | n := len(h) - 1 59 | if n != i { 60 | heapSwap(h, i, n) 61 | if !heapDown(h, i, n) { 62 | heapUp(h, i) 63 | } 64 | } 65 | *heap = h[0:n] 66 | return h[n] 67 | } 68 | 69 | func heapSwap[T any](heap []T, i, j int) { 70 | heap[i], heap[j] = heap[j], heap[i] 71 | } 72 | 73 | func heapUp[T Ordered](heap []T, j int) { 74 | for { 75 | i := (j - 1) / 2 // parent 76 | if i == j || !(heap[j] < heap[i]) { 77 | break 78 | } 79 | heapSwap(heap, i, j) 80 | j = i 81 | } 82 | } 83 | 84 | func heapDown[T Ordered](heap []T, i0, n int) bool { 85 | i := i0 86 | for { 87 | j1 := 2*i + 1 88 | if j1 >= n || j1 < 0 { // j1 < 0 after int overflow 89 | break 90 | } 91 | j := j1 // left child 92 | if j2 := j1 + 1; j2 < n && heap[j2] < heap[j1] { 93 | j = j2 // = 2*i + 2 // right child 94 | } 95 | if !(heap[j] < heap[i]) { 96 | break 97 | } 98 | heapSwap(heap, i, j) 99 | i = j 100 | } 101 | return i > i0 102 | } 103 | 104 | // MakeHeapFunc build a min-heap on slice array with compare function less. 105 | // 106 | // Complexity: O(len(array)) 107 | func MakeHeapFunc[T any](array []T, less LessFn[T]) { 108 | // heapify 109 | n := len(array) 110 | for i := n/2 - 1; i >= 0; i-- { 111 | heapDownFunc(array, i, n, less) 112 | } 113 | } 114 | 115 | // IsHeapFunc checks whether the elements in slice array are a min heap (accord to less). 116 | // 117 | // Complexity: O(len(array)). 118 | func IsHeapFunc[T any](array []T, less LessFn[T]) bool { 119 | parent := 0 120 | for child := 1; child < len(array); child++ { 121 | if !less(array[parent], array[child]) { 122 | return false 123 | } 124 | 125 | if (child & 1) == 0 { 126 | parent++ 127 | } 128 | 129 | } 130 | return true 131 | } 132 | 133 | // PushHeapFunc pushes a element v into the heap. 134 | // 135 | // Complexity: O(log(len(*heap))). 136 | func PushHeapFunc[T any](heap *[]T, v T, less LessFn[T]) { 137 | *heap = append(*heap, v) 138 | heapUpFunc(*heap, len(*heap)-1, less) 139 | } 140 | 141 | // PopHeapFunc removes and returns the minimum (according to less) element from the heap. 142 | // 143 | // Complexity: O(log n) where n = len(*heap). 144 | func PopHeapFunc[T any](heap *[]T, less LessFn[T]) T { 145 | h := *heap 146 | n := len(h) - 1 147 | heapSwap(h, 0, n) 148 | heapDownFunc(h, 0, n, less) 149 | *heap = h[0:n] 150 | return h[n] 151 | } 152 | 153 | // RemoveHeapFunc removes and returns the element at index i from the heap. 154 | // 155 | // Complexity: is O(log(n)) where n = len(*heap). 156 | func RemoveHeapFunc[T any](heap *[]T, i int, less LessFn[T]) T { 157 | h := *heap 158 | n := len(h) - 1 159 | if n != i { 160 | heapSwap(h, i, n) 161 | if !heapDownFunc(h, i, n, less) { 162 | heapUpFunc(h, i, less) 163 | } 164 | } 165 | *heap = h[0:n] 166 | return h[n] 167 | } 168 | 169 | func heapUpFunc[T any](heap []T, j int, less LessFn[T]) { 170 | for { 171 | i := (j - 1) / 2 // parent 172 | if i == j || !less(heap[j], heap[i]) { 173 | break 174 | } 175 | heapSwap(heap, i, j) 176 | j = i 177 | } 178 | } 179 | 180 | func heapDownFunc[T any](heap []T, i0, n int, less LessFn[T]) bool { 181 | i := i0 182 | for { 183 | j1 := 2*i + 1 184 | if j1 >= n || j1 < 0 { // j1 < 0 after int overflow 185 | break 186 | } 187 | j := j1 // left child 188 | if j2 := j1 + 1; j2 < n && less(heap[j2], heap[j1]) { 189 | j = j2 // = 2*i + 2 // right child 190 | } 191 | if !less(heap[j], heap[i]) { 192 | break 193 | } 194 | heapSwap(heap, i, j) 195 | i = j 196 | } 197 | return i > i0 198 | } 199 | -------------------------------------------------------------------------------- /heap.md: -------------------------------------------------------------------------------- 1 | # Heap 2 | 3 | stl4go provides a group of [heap](https://en.wikipedia.org/wiki/Heap_(data_structure)) algorithms. 4 | 5 | ## Easy to use 6 | 7 | For ordered types, you can easily use the heap algorithms, for example, with `int` type: 8 | 9 | ```go 10 | func Example() { 11 | heap := []int {5, 4, 3, 2, 1} 12 | stl4go.MakeMinHeap(heap) 13 | stl4go.PushMinHeap(&heap, 6) 14 | n := stl4go.PopMinHeap(&heap) // get 1 15 | } 16 | ``` 17 | 18 | Please compare it with [container/heap](https://pkg.go.dev/container/heap#example-package-IntHeap): 19 | 20 | ```go 21 | // This example demonstrates an integer heap built using the heap interface. 22 | package main 23 | 24 | import ( 25 | "container/heap" 26 | "fmt" 27 | ) 28 | 29 | // An IntHeap is a min-heap of ints. 30 | type IntHeap []int 31 | 32 | func (h IntHeap) Len() int { return len(h) } 33 | func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] } 34 | func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 35 | 36 | func (h *IntHeap) Push(x any) { 37 | // Push and Pop use pointer receivers because they modify the slice's length, 38 | // not just its contents. 39 | *h = append(*h, x.(int)) 40 | } 41 | 42 | func (h *IntHeap) Pop() any { 43 | old := *h 44 | n := len(old) 45 | x := old[n-1] 46 | *h = old[0 : n-1] 47 | return x 48 | } 49 | 50 | // This example inserts several ints into an IntHeap, checks the minimum, 51 | // and removes them in order of priority. 52 | func main() { 53 | h := &IntHeap{2, 1, 5} 54 | heap.Init(h) 55 | heap.Push(h, 3) 56 | fmt.Printf("minimum: %d\n", (*h)[0]) 57 | for h.Len() > 0 { 58 | fmt.Printf("%d ", heap.Pop(h)) 59 | } 60 | } 61 | ``` 62 | 63 | You must define a new type `IntHeap` and five methods before to use the standard library, 64 | these boilerplate codes are verbose, tedious, and boring. 65 | 66 | ## Benchmark 67 | 68 | The heap algorithms are also much faster than `container/heap`, even for the `Func` version.: 69 | 70 | ```console 71 | % go test -bench BenchmarkHeap -benchmem 72 | goos: darwin 73 | goarch: arm64 74 | pkg: github.com/chen3feng/stl4go 75 | BenchmarkHeapInit/container/heap.Init-10 360126 3195 ns/op 0 B/op 0 allocs/op 76 | BenchmarkHeapInit/stlgo.MakeMinHeap-10 998325 1127 ns/op 0 B/op 0 allocs/op 77 | BenchmarkHeapInit/stlgo.MakeHeapFunc-10 488419 2355 ns/op 0 B/op 0 allocs/op 78 | ... 79 | BenchmarkHeapPush/container/heap.Push-10 101260 11630 ns/op 5952 B/op 744 allocs/op 80 | BenchmarkHeapPush/stlgo.PushMinHeap-10 511680 2261 ns/op 0 B/op 0 allocs/op 81 | BenchmarkHeapPush/stlgo.PushHeapFunc-10 445850 2625 ns/op 0 B/op 0 allocs/op 82 | ... 83 | BenchmarkHeapPop/container/heap.Pop-10 14709 81153 ns/op 5952 B/op 744 allocs/op 84 | BenchmarkHeapPop/stlgo.PopMinHeap-10 61608 19516 ns/op 0 B/op 0 allocs/op 85 | BenchmarkHeapPop/stlgo.PopHeapFunc-10 22887 52440 ns/op 0 B/op 0 allocs/op 86 | PASS 87 | ok github.com/chen3feng/stl4go 19.827s 88 | ``` 89 | -------------------------------------------------------------------------------- /heap_bench_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "container/heap" 5 | "testing" 6 | ) 7 | 8 | // An intHeap is a min-heap of ints. 9 | type intHeap []int 10 | 11 | func (h intHeap) Len() int { return len(h) } 12 | func (h intHeap) Less(i, j int) bool { return h[i] < h[j] } 13 | func (h intHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 14 | func (h *intHeap) Push(x any) { *h = append(*h, x.(int)) } 15 | 16 | func (h *intHeap) Pop() any { 17 | old := *h 18 | n := len(old) 19 | x := old[n-1] 20 | *h = old[0 : n-1] 21 | return x 22 | } 23 | 24 | func BenchmarkHeapInit(b *testing.B) { 25 | data := Range(1, 1000) 26 | Shuffle(data) 27 | b.Run("container/heap.Init", func(b *testing.B) { 28 | data = Copy(data) 29 | h := intHeap(data) 30 | b.ResetTimer() 31 | for i := 0; i < b.N; i++ { 32 | heap.Init(&h) 33 | } 34 | }) 35 | b.Run("stlgo.MakeMinHeap", func(b *testing.B) { 36 | data = Copy(data) 37 | b.ResetTimer() 38 | for i := 0; i < b.N; i++ { 39 | MakeMinHeap(data) 40 | } 41 | }) 42 | b.Run("stlgo.MakeHeapFunc", func(b *testing.B) { 43 | data = Copy(data) 44 | b.ResetTimer() 45 | for i := 0; i < b.N; i++ { 46 | MakeHeapFunc(data, Less[int]) 47 | } 48 | }) 49 | } 50 | 51 | func BenchmarkHeapPush(b *testing.B) { 52 | const count = 1000 53 | b.Run("container/heap.Push", func(b *testing.B) { 54 | h := intHeap([]int{}) 55 | for i := 0; i < b.N; i++ { 56 | h = h[0:0] 57 | for n := 0; n < count; n++ { 58 | heap.Push(&h, n) 59 | } 60 | } 61 | }) 62 | b.Run("stlgo.PushMinHeap", func(b *testing.B) { 63 | h := []int{} 64 | for i := 0; i < b.N; i++ { 65 | h = h[0:0] 66 | for n := 0; n < count; n++ { 67 | PushMinHeap(&h, n) 68 | } 69 | } 70 | }) 71 | b.Run("stlgo.PushHeapFunc", func(b *testing.B) { 72 | h := []int{} 73 | for i := 0; i < b.N; i++ { 74 | h = h[0:0] 75 | for n := 0; n < count; n++ { 76 | PushHeapFunc(&h, n, Less[int]) 77 | } 78 | } 79 | }) 80 | } 81 | 82 | func BenchmarkHeapPop(b *testing.B) { 83 | s := Range(1, 1000) 84 | b.Run("container/heap.Pop", func(b *testing.B) { 85 | for i := 0; i < b.N; i++ { 86 | b.StopTimer() 87 | h := intHeap(Copy(s)) 88 | heap.Init(&h) 89 | b.StartTimer() 90 | for len(h) > 0 { 91 | _ = heap.Pop(&h).(int) 92 | } 93 | } 94 | }) 95 | b.Run("stlgo.PopMinHeap", func(b *testing.B) { 96 | for i := 0; i < b.N; i++ { 97 | b.StopTimer() 98 | h := Copy(s) 99 | MakeMinHeap(h) 100 | b.StartTimer() 101 | for len(h) > 0 { 102 | _ = PopMinHeap(&h) 103 | } 104 | } 105 | }) 106 | b.Run("stlgo.PopHeapFunc", func(b *testing.B) { 107 | for i := 0; i < b.N; i++ { 108 | b.StopTimer() 109 | h := Copy(s) 110 | MakeMinHeap(h) 111 | b.StartTimer() 112 | for len(h) > 0 { 113 | _ = PopHeapFunc(&h, Less[int]) 114 | } 115 | } 116 | }) 117 | } 118 | -------------------------------------------------------------------------------- /heap_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestMakeMinHeap(t *testing.T) { 8 | data := []int{5, 4, 3, 2, 1} 9 | expectFalse(t, IsMinHeap(data)) 10 | MakeMinHeap(data) 11 | expectTrue(t, IsMinHeap(data)) 12 | } 13 | 14 | func TestIsMinHeap(t *testing.T) { 15 | heap := []int{} 16 | expectTrue(t, IsMinHeap(heap)) 17 | heap = append(heap, 1) 18 | expectTrue(t, IsMinHeap(heap)) 19 | } 20 | 21 | func TestMakeHeapFunc(t *testing.T) { 22 | data := []int{1, 2, 3, 4, 5} 23 | expectFalse(t, IsHeapFunc(data, Greater[int])) 24 | MakeHeapFunc(data, Greater[int]) 25 | expectTrue(t, IsHeapFunc(data, Greater[int])) 26 | } 27 | 28 | func Test_MinHeap_PushPop(t *testing.T) { 29 | heap := []int{} 30 | PushMinHeap(&heap, 1) 31 | expectEq(t, PopMinHeap(&heap), 1) 32 | expectTrue(t, IsMinHeap(heap)) 33 | } 34 | 35 | func Test_HeapFunc_PushPop(t *testing.T) { 36 | heap := []int{} 37 | cmp := Greater[int] 38 | PushHeapFunc(&heap, 1, cmp) 39 | expectTrue(t, IsHeapFunc(heap, cmp)) 40 | expectEq(t, PopHeapFunc(&heap, cmp), 1) 41 | expectTrue(t, IsHeapFunc(heap, cmp)) 42 | } 43 | 44 | func Test_MinHeap_Remove(t *testing.T) { 45 | heap := []int{5, 4, 3, 2, 1} 46 | MakeMinHeap(heap) 47 | RemoveMinHeap(&heap, 1) 48 | expectTrue(t, IsMinHeap(heap)) 49 | } 50 | 51 | func Test_HeapFunc_Remove(t *testing.T) { 52 | heap := []int{1, 2, 3, 4, 5} 53 | cmp := Greater[int] 54 | MakeHeapFunc(heap, cmp) 55 | RemoveHeapFunc(&heap, 1, cmp) 56 | expectTrue(t, IsHeapFunc(heap, cmp)) 57 | } 58 | -------------------------------------------------------------------------------- /helper.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | // nameOfType return type name as a string. 8 | func nameOfType[T any]() string { 9 | var t *T 10 | return reflect.TypeOf(t).String()[1:] 11 | } 12 | -------------------------------------------------------------------------------- /iterator.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | // Iterator is the interface for container's iterator. 4 | type Iterator[T any] interface { 5 | IsNotEnd() bool // Whether it is point to the end of the range. 6 | MoveToNext() // Let it point to the next element. 7 | Value() T // Return the value of current element. 8 | } 9 | 10 | // MutableIterator is the interface for container's mutable iterator. 11 | type MutableIterator[T any] interface { 12 | Iterator[T] 13 | Pointer() *T // Return the pointer to the value of current element. 14 | } 15 | 16 | // MapIterator is the interface for map's iterator. 17 | type MapIterator[K any, V any] interface { 18 | Iterator[V] 19 | Key() K // The key of the element 20 | } 21 | 22 | // MutableMapIterator is the interface for map's mutable iterator. 23 | type MutableMapIterator[K any, V any] interface { 24 | MutableIterator[V] 25 | Key() K // The key of the element 26 | } 27 | -------------------------------------------------------------------------------- /lookup.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | // Max return the larger value between `a` and `b`. 4 | // 5 | // Complexity: O(1). 6 | func Max[T Ordered](a, b T) T { 7 | if a > b { 8 | return a 9 | } 10 | return b 11 | } 12 | 13 | // Min return the smaller value between `a` and `b`. 14 | // 15 | // Complexity: O(1). 16 | func Min[T Ordered](a, b T) T { 17 | if a < b { 18 | return a 19 | } 20 | return b 21 | } 22 | 23 | // MaxN return the maximum value in the sequence `a`. 24 | // 25 | // Complexity: O(len(a)). 26 | func MaxN[T Ordered](a ...T) T { 27 | if len(a) == 0 { 28 | panic("can't call MaxN() with empty arguments list") 29 | } 30 | v := a[0] 31 | for i := 1; i < len(a); i++ { 32 | if a[i] > v { 33 | v = a[i] 34 | } 35 | } 36 | return v 37 | } 38 | 39 | // MinN return the minimum value in the sequence `a`. 40 | // 41 | // Complexity: O(len(a)). 42 | func MinN[T Ordered](a ...T) T { 43 | if len(a) == 0 { 44 | panic("can't call MaxN() with empty arguments list") 45 | } 46 | v := a[0] 47 | for i := 1; i < len(a); i++ { 48 | if a[i] < v { 49 | v = a[i] 50 | } 51 | } 52 | return v 53 | } 54 | 55 | // MinMax returns both min and max between a and b. 56 | // 57 | // Complexity: O(1). 58 | func MinMax[T Ordered](a, b T) (min, max T) { 59 | if a < b { 60 | return a, b 61 | } 62 | return b, a 63 | } 64 | 65 | // MinMaxN returns both min and max in slice a. 66 | // 67 | // Complexity: O(len(a)) 68 | func MinMaxN[T Ordered](a ...T) (min, max T) { 69 | if len(a) == 0 { 70 | panic("can't call MaxN() with empty arguments list") 71 | } 72 | min = a[0] 73 | max = a[0] 74 | for i := 1; i < len(a); i++ { 75 | if a[i] < min { 76 | min = a[i] 77 | } 78 | if a[i] > max { 79 | max = a[i] 80 | } 81 | } 82 | return 83 | } 84 | 85 | // Find find the first value x in the given slice a linearly. 86 | // return (index, true) if found, 87 | // return (_, false) if not found. 88 | // 89 | // Complexity: O(len(a)). 90 | func Find[T comparable](a []T, x T) (index int, ok bool) { 91 | for i, v := range a { 92 | if v == x { 93 | return i, true 94 | } 95 | } 96 | return -1, false 97 | } 98 | 99 | // FindIf find the first value x satisfying function cond in the given slice a linearly. 100 | // return (index, true) if found, 101 | // return (_, false) if not found. 102 | // 103 | // Complexity: O(len(a)). 104 | func FindIf[T any](a []T, cond func(T) bool) (index int, ok bool) { 105 | for i, v := range a { 106 | if cond(v) { 107 | return i, true 108 | } 109 | } 110 | return -1, false 111 | } 112 | 113 | // Index find the value x in the given slice a linearly. 114 | // 115 | // Return index if found, -1 if not found. 116 | // 117 | // Complexity: O(len(a)). 118 | func Index[T comparable](a []T, x T) int { 119 | for i, v := range a { 120 | if v == x { 121 | return i 122 | } 123 | } 124 | return -1 125 | } 126 | 127 | // AllOf return true if pred(e) returns true for all elements e in a. 128 | // 129 | // Complexity: O(len(a)). 130 | func AllOf[T any](a []T, pred func(T) bool) bool { 131 | for _, v := range a { 132 | if !pred(v) { 133 | return false 134 | } 135 | } 136 | return true 137 | } 138 | 139 | // AnyOf return true if pred(e) returns true for any elements e in a. 140 | // 141 | // Complexity: O(len(a)). 142 | func AnyOf[T any](a []T, pred func(T) bool) bool { 143 | for _, v := range a { 144 | if pred(v) { 145 | return true 146 | } 147 | } 148 | return false 149 | } 150 | 151 | // NoneOf return true pred(e) returns true for none elements e in a. 152 | // 153 | // Complexity: O(len(a)). 154 | func NoneOf[T any](a []T, pred func(T) bool) bool { 155 | return !AnyOf(a, pred) 156 | } 157 | -------------------------------------------------------------------------------- /lookup_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import "testing" 4 | 5 | func Test_Min(t *testing.T) { 6 | expectEq(t, Min(1, 2), 1) 7 | expectEq(t, Min(2, 1), 1) 8 | expectEq(t, Min(1, 1), 1) 9 | expectEq(t, Min("hello", "world"), "hello") 10 | } 11 | 12 | func Test_Max(t *testing.T) { 13 | expectEq(t, Max(1, 2), 2) 14 | expectEq(t, Max(2, 1), 2) 15 | expectEq(t, Max(2, 2), 2) 16 | expectEq(t, Max("hello", "world"), "world") 17 | } 18 | 19 | func Test_MinN(t *testing.T) { 20 | expectEq(t, MinN(1, 2, 3), 1) 21 | expectEq(t, MinN(2, 1, 3), 1) 22 | expectEq(t, MinN(1, 1, 1), 1) 23 | expectEq(t, MinN("hello", "world"), "hello") 24 | expectPanic(t, func() { MinN(emptyInts...) }) 25 | } 26 | 27 | func Test_MaxN(t *testing.T) { 28 | expectEq(t, MaxN(1, 2), 2) 29 | expectEq(t, MaxN(2, 1), 2) 30 | expectEq(t, MaxN(2, 2), 2) 31 | expectEq(t, MaxN("hello", "world"), "world") 32 | expectPanic(t, func() { MaxN(emptyInts...) }) 33 | } 34 | 35 | func Test_MinMax(t *testing.T) { 36 | min, max := MinMax(1, 2) 37 | expectEq(t, min, 1) 38 | expectEq(t, max, 2) 39 | min, max = MinMax(2, 1) 40 | expectEq(t, min, 1) 41 | expectEq(t, max, 2) 42 | } 43 | 44 | func Test_MinMaxN(t *testing.T) { 45 | min, max := MinMaxN(3, 4, 1, 2) 46 | expectEq(t, min, 1) 47 | expectEq(t, max, 4) 48 | expectPanic(t, func() { MinMaxN(emptyInts...) }) 49 | } 50 | 51 | func Test_Find(t *testing.T) { 52 | a := []int{1, 2, 3, 4, 3} 53 | i, ok := Find(a, 3) 54 | expectTrue(t, ok) 55 | expectEq(t, i, 2) 56 | i, ok = Find(a, 5) 57 | expectFalse(t, ok) 58 | } 59 | 60 | func Test_FindIf(t *testing.T) { 61 | isNeg := func(x int) bool { return x < 0 } 62 | a := []int{1, 2, -3, 4, 3} 63 | i, ok := FindIf(a, isNeg) 64 | expectTrue(t, ok) 65 | expectEq(t, i, 2) 66 | i, ok = FindIf([]int{1, 2, 3, 4, 3}, isNeg) 67 | expectFalse(t, ok) 68 | } 69 | 70 | func Test_Index(t *testing.T) { 71 | a := []int{1, 2, 3, 4, 3} 72 | expectEq(t, Index(a, 3), 2) 73 | expectEq(t, Index(a, 5), -1) 74 | } 75 | 76 | var ( 77 | pos = []int{1, 2, 3, 4, 5} 78 | neg = []int{-1, -2, -3, -4, -5} 79 | mix = []int{1, -2, 3, -4, 5} 80 | ) 81 | 82 | func isNegative(n int) bool { return n < 0 } 83 | 84 | func Test_AllOf(t *testing.T) { 85 | expectFalse(t, AllOf(pos, isNegative)) 86 | expectTrue(t, AllOf(neg, isNegative)) 87 | expectFalse(t, AllOf(mix, isNegative)) 88 | } 89 | 90 | func Test_AnyOf(t *testing.T) { 91 | expectFalse(t, AnyOf(pos, isNegative)) 92 | expectTrue(t, AnyOf(neg, isNegative)) 93 | expectTrue(t, AnyOf(mix, isNegative)) 94 | } 95 | 96 | func Test_NoneOf(t *testing.T) { 97 | expectTrue(t, NoneOf(pos, isNegative)) 98 | expectFalse(t, NoneOf(neg, isNegative)) 99 | expectFalse(t, NoneOf(mix, isNegative)) 100 | } 101 | -------------------------------------------------------------------------------- /mlc_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "aliveStatusCodes": [ 3 | 400, 4 | 429, 5 | 200 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /pool.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import "sync" 4 | 5 | // Pool is a type safed sync.Pool. 6 | type Pool[T any] sync.Pool 7 | 8 | // MakePool returns a Pool object 9 | func MakePool[T any]() Pool[T] { 10 | return Pool[T]{New: func() any { return new(T) }} 11 | } 12 | 13 | // MakePoolWithNew returns a Pool object with specified new function. 14 | func MakePoolWithNew[T any](new func() *T) Pool[T] { 15 | if new != nil { 16 | return Pool[T]{New: func() any { return new() }} 17 | } 18 | return Pool[T]{} 19 | } 20 | 21 | // Get selects an arbitrary item from the Pool, removes it from the Pool, and returns it to the caller. 22 | func (pool *Pool[T]) Get() *T { 23 | i := pool.untyped().Get() 24 | if i == nil { 25 | return nil 26 | } 27 | return i.(*T) 28 | } 29 | 30 | // Put puts x to the pool. 31 | func (pool *Pool[T]) Put(x *T) { 32 | if x != nil { 33 | pool.untyped().Put(x) 34 | } 35 | } 36 | 37 | func (pool *Pool[T]) untyped() *sync.Pool { 38 | return (*sync.Pool)(pool) 39 | } 40 | -------------------------------------------------------------------------------- /pool_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import "testing" 4 | 5 | func TestPool(t *testing.T) { 6 | p := MakePool[int]() 7 | x := p.Get() 8 | p.Put(x) 9 | } 10 | 11 | func TestMakePoolWithNew(t *testing.T) { 12 | p := MakePoolWithNew(func() *int { return new(int) }) 13 | x := p.Get() 14 | p.Put(x) 15 | } 16 | 17 | func TestMakePoolNil(t *testing.T) { 18 | p := MakePoolWithNew[int](nil) 19 | x := p.Get() 20 | expectEq(t, x, nil) 21 | p.Put(x) 22 | } 23 | -------------------------------------------------------------------------------- /priority_queue.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | // PriorityQueue is an queue with priority. 4 | // The elements of the priority queue are ordered according to their natural ordering, 5 | // or by a less function provided at construction time, depending on which constructor is used. 6 | type PriorityQueue[T any] struct { 7 | heap []T 8 | impl pqImpl[T] 9 | } 10 | 11 | // NewPriorityQueue creates an empty priority object. 12 | func NewPriorityQueue[T Ordered]() *PriorityQueue[T] { 13 | pq := pqOrdered[T]{} 14 | pq.impl = (pqImpl[T])(&pq) 15 | return &pq.PriorityQueue 16 | } 17 | 18 | // NewPriorityQueueOn creates a new priority object on the specified slices. 19 | // The slice become a heap after the call. 20 | func NewPriorityQueueOn[T Ordered](slice []T) *PriorityQueue[T] { 21 | MakeMinHeap(slice) 22 | pq := pqOrdered[T]{} 23 | pq.heap = slice 24 | pq.impl = pqImpl[T](&pq) 25 | return &pq.PriorityQueue 26 | } 27 | 28 | // NewPriorityQueueOf creates a new priority object with specified initial elements. 29 | func NewPriorityQueueOf[T Ordered](elements ...T) *PriorityQueue[T] { 30 | return NewPriorityQueueOn(elements) 31 | } 32 | 33 | // NewPriorityQueueFunc creates an empty priority object with specified compare function less. 34 | func NewPriorityQueueFunc[T any](less LessFn[T]) *PriorityQueue[T] { 35 | pq := pqFunc[T]{} 36 | pq.less = less 37 | pq.impl = (pqImpl[T])(&pq) 38 | return &pq.PriorityQueue 39 | } 40 | 41 | // Len returns the number of elements in the priority queue. 42 | func (pq *PriorityQueue[T]) Len() int { 43 | return len(pq.heap) 44 | } 45 | 46 | // IsEmpty checks whether priority queue has no elements. 47 | func (pq *PriorityQueue[T]) IsEmpty() bool { 48 | return len(pq.heap) == 0 49 | } 50 | 51 | // Clear clear the priority queue. 52 | func (pq *PriorityQueue[T]) Clear() { 53 | pq.heap = pq.heap[0:0] 54 | } 55 | 56 | // Top returns the top element in the priority queue. 57 | func (pq *PriorityQueue[T]) Top() T { 58 | return pq.heap[0] 59 | } 60 | 61 | // Push pushes the given element v to the priority queue. 62 | func (pq *PriorityQueue[T]) Push(v T) { 63 | pq.impl.Push(v) 64 | } 65 | 66 | // Pop removes the top element in the priority queue. 67 | func (pq *PriorityQueue[T]) Pop() T { 68 | return pq.impl.Pop() 69 | } 70 | 71 | type pqImpl[T any] interface { 72 | Push(v T) 73 | Pop() T 74 | } 75 | 76 | type pqOrdered[T Ordered] struct { 77 | PriorityQueue[T] 78 | } 79 | 80 | func (pq *pqOrdered[T]) Push(v T) { 81 | PushMinHeap(&pq.heap, v) 82 | } 83 | 84 | func (pq *pqOrdered[T]) Pop() T { 85 | return PopMinHeap(&pq.heap) 86 | } 87 | 88 | // funcHeap is a min-heap of T compared with less. 89 | type pqFunc[T any] struct { 90 | PriorityQueue[T] 91 | less LessFn[T] 92 | } 93 | 94 | func (pq *pqFunc[T]) Push(v T) { 95 | PushHeapFunc(&pq.heap, v, pq.less) 96 | } 97 | 98 | func (pq *pqFunc[T]) Pop() T { 99 | return PopHeapFunc(&pq.heap, pq.less) 100 | } 101 | -------------------------------------------------------------------------------- /priority_queue_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestInterface(t *testing.T) { 9 | _ = (Container)(NewPriorityQueue[int]()) 10 | } 11 | 12 | func TestNewPriorityQueue(t *testing.T) { 13 | pq := NewPriorityQueue[int]() 14 | expectTrue(t, pq.IsEmpty()) 15 | expectEq(t, pq.Len(), 0) 16 | } 17 | 18 | func TestNewPriorityQueueOf(t *testing.T) { 19 | pq := NewPriorityQueueOf(5, 4, 3, 2, 1) 20 | expectEq(t, pq.Len(), 5) 21 | } 22 | 23 | func TestPriorityQueue_PushPop(t *testing.T) { 24 | less := Less[int] 25 | pq := NewPriorityQueueFunc(less) 26 | for i := 5; i > 0; i-- { 27 | pq.Push(i) 28 | expectFalse(t, pq.IsEmpty()) 29 | } 30 | var elements []int 31 | for !pq.IsEmpty() { 32 | elements = append(elements, pq.Pop()) 33 | } 34 | expectTrue(t, pq.IsEmpty()) 35 | expectTrue(t, IsSorted(elements)) 36 | expectEq(t, len(elements), 5) 37 | } 38 | 39 | func TestPriorityQueueFunc_PushPop(t *testing.T) { 40 | pq := NewPriorityQueueFunc(Less[int]) 41 | for i := 5; i > 0; i-- { 42 | pq.Push(i) 43 | } 44 | var elements []int 45 | for !pq.IsEmpty() { 46 | elements = append(elements, pq.Pop()) 47 | } 48 | expectTrue(t, pq.IsEmpty()) 49 | expectTrue(t, IsSorted(elements)) 50 | expectEq(t, len(elements), 5) 51 | } 52 | 53 | func TestPriorityQueue_Clear(t *testing.T) { 54 | pq := NewPriorityQueue[int]() 55 | pq.Clear() 56 | expectTrue(t, pq.IsEmpty()) 57 | expectEq(t, pq.Len(), 0) 58 | pq = NewPriorityQueueOf(1, 2, 3) 59 | pq.Clear() 60 | expectTrue(t, pq.IsEmpty()) 61 | expectEq(t, pq.Len(), 0) 62 | } 63 | 64 | // This example inserts several ints into an IntHeap, checks the minimum, 65 | // and removes them in order of priority. 66 | func ExamplePriorityQueue() { 67 | h := NewPriorityQueue[int]() 68 | h.Push(3) 69 | h.Push(2) 70 | h.Push(1) 71 | h.Push(5) 72 | fmt.Printf("minimum: %d\n", h.Top()) 73 | 74 | for h.Len() > 0 { 75 | fmt.Printf("%d ", h.Pop()) 76 | } 77 | // Output: 78 | // minimum: 1 79 | // 1 2 3 5 80 | } 81 | -------------------------------------------------------------------------------- /skiplist.go: -------------------------------------------------------------------------------- 1 | // This implementation is based on https://github.com/liyue201/gostl/tree/master/ds/skiplist 2 | // (many thanks), added many optimizations, such as: 3 | // 4 | // - adaptive level 5 | // - lesser search for prevs when key already exists. 6 | // - reduce memory allocations 7 | // - richer interface. 8 | // 9 | // etc. 10 | 11 | package stl4go 12 | 13 | import ( 14 | "math/bits" 15 | "math/rand" 16 | "time" 17 | ) 18 | 19 | const ( 20 | skipListMaxLevel = 40 21 | ) 22 | 23 | // SkipList is a probabilistic data structure that seem likely to supplant balanced trees as the 24 | // implementation method of choice for many applications. Skip list algorithms have the same 25 | // asymptotic expected time bounds as balanced trees and are simpler, faster and use less space. 26 | // 27 | // See https://en.wikipedia.org/wiki/Skip_list for more details. 28 | type SkipList[K any, V any] struct { 29 | level int // Current level, may increase dynamically during insertion 30 | len int // Total elements number in the skiplist. 31 | head skipListNode[K, V] // head.next[level] is the head of each level. 32 | // This cache is used to save the previous nodes when modifying the skip list to avoid 33 | // allocating memory each time it is called. 34 | prevsCache []*skipListNode[K, V] 35 | rander *rand.Rand 36 | impl skipListImpl[K, V] 37 | } 38 | 39 | // NewSkipList creates a new SkipList for Ordered key type. 40 | func NewSkipList[K Ordered, V any]() *SkipList[K, V] { 41 | sl := skipListOrdered[K, V]{} 42 | sl.init() 43 | sl.impl = (skipListImpl[K, V])(&sl) 44 | return &sl.SkipList 45 | } 46 | 47 | // NewSkipListFromMap creates a new SkipList from a map. 48 | func NewSkipListFromMap[K Ordered, V any](m map[K]V) *SkipList[K, V] { 49 | sl := NewSkipList[K, V]() 50 | for k, v := range m { 51 | sl.Insert(k, v) 52 | } 53 | return sl 54 | } 55 | 56 | // NewSkipListFunc creates a new SkipList with specified compare function keyCmp. 57 | func NewSkipListFunc[K any, V any](keyCmp CompareFn[K]) *SkipList[K, V] { 58 | sl := skipListFunc[K, V]{} 59 | sl.init() 60 | sl.keyCmp = keyCmp 61 | sl.impl = skipListImpl[K, V](&sl) 62 | return &sl.SkipList 63 | } 64 | 65 | // IsEmpty implements the Container interface. 66 | func (sl *SkipList[K, V]) IsEmpty() bool { 67 | return sl.len == 0 68 | } 69 | 70 | // Len implements the Container interface. 71 | func (sl *SkipList[K, V]) Len() int { 72 | return sl.len 73 | } 74 | 75 | // Clear implements the Container interface. 76 | func (sl *SkipList[K, V]) Clear() { 77 | for i := range sl.head.next { 78 | sl.head.next[i] = nil 79 | } 80 | sl.level = 1 81 | sl.len = 0 82 | } 83 | 84 | // Iterate return an iterator to the skiplist. 85 | func (sl *SkipList[K, V]) Iterate() MutableMapIterator[K, V] { 86 | return &skipListIterator[K, V]{sl.head.next[0], nil} 87 | } 88 | 89 | // Insert inserts a key-value pair into the skiplist. 90 | // If the key is already in the skip list, it's value will be updated. 91 | func (sl *SkipList[K, V]) Insert(key K, value V) { 92 | node, prevs := sl.impl.findInsertPoint(key) 93 | 94 | if node != nil { 95 | // Already exist, update the value 96 | node.value = value 97 | return 98 | } 99 | 100 | level := sl.randomLevel() 101 | node = newSkipListNode(level, key, value) 102 | 103 | // Insert node to each level 104 | for i := 0; i < Min(level, sl.level); i++ { 105 | node.next[i] = prevs[i].next[i] 106 | prevs[i].next[i] = node 107 | } 108 | 109 | if level > sl.level { 110 | // Increase the level 111 | for i := sl.level; i < level; i++ { 112 | sl.head.next[i] = node 113 | } 114 | sl.level = level 115 | } 116 | 117 | sl.len++ 118 | } 119 | 120 | // Find returns the value associated with the passed key if the key is in the skiplist, otherwise 121 | // returns nil. 122 | func (sl *SkipList[K, V]) Find(key K) *V { 123 | node := sl.impl.findNode(key) 124 | if node != nil { 125 | return &node.value 126 | } 127 | return nil 128 | } 129 | 130 | // Has implement the Map interface. 131 | func (sl *SkipList[K, V]) Has(key K) bool { 132 | return sl.impl.findNode(key) != nil 133 | } 134 | 135 | // LowerBound returns an iterator to the first element in the skiplist that 136 | // does not satisfy element < value (i.e. greater or equal to), 137 | // or a end iterator if no such element is found. 138 | func (sl *SkipList[K, V]) LowerBound(key K) MutableMapIterator[K, V] { 139 | return &skipListIterator[K, V]{sl.impl.lowerBound(key), nil} 140 | } 141 | 142 | // UpperBound returns an iterator to the first element in the skiplist that 143 | // does not satisfy value < element (i.e. strictly greater), 144 | // or a end iterator if no such element is found. 145 | func (sl *SkipList[K, V]) UpperBound(key K) MutableMapIterator[K, V] { 146 | return &skipListIterator[K, V]{sl.impl.upperBound(key), nil} 147 | } 148 | 149 | // FindRange returns an iterator in range [first, last) (last is not included). 150 | func (sl *SkipList[K, V]) FindRange(first, last K) MutableMapIterator[K, V] { 151 | return &skipListIterator[K, V]{sl.impl.lowerBound(first), sl.impl.upperBound(last)} 152 | } 153 | 154 | // Remove removes the key-value pair associated with the passed key and returns true if the key is 155 | // in the skiplist, otherwise returns false. 156 | func (sl *SkipList[K, V]) Remove(key K) bool { 157 | node, prevs := sl.impl.findRemovePoint(key) 158 | if node == nil { 159 | return false 160 | } 161 | for i, v := range node.next { // Nomove the node from each level's links. 162 | prevs[i].next[i] = v 163 | } 164 | // Decrease the level if the top level become empty 165 | for sl.level > 1 && sl.head.next[sl.level-1] == nil { 166 | sl.level-- 167 | } 168 | sl.len-- 169 | return true 170 | } 171 | 172 | // ForEach implements the Map interface. 173 | func (sl *SkipList[K, V]) ForEach(op func(K, V)) { 174 | for e := sl.head.next[0]; e != nil; e = e.next[0] { 175 | op(e.key, e.value) 176 | } 177 | } 178 | 179 | // ForEachMutable implements the Map interface. 180 | func (sl *SkipList[K, V]) ForEachMutable(op func(K, *V)) { 181 | for e := sl.head.next[0]; e != nil; e = e.next[0] { 182 | op(e.key, &e.value) 183 | } 184 | } 185 | 186 | // ForEachIf implements the Map interface. 187 | func (sl *SkipList[K, V]) ForEachIf(op func(K, V) bool) { 188 | for e := sl.head.next[0]; e != nil; e = e.next[0] { 189 | if !op(e.key, e.value) { 190 | return 191 | } 192 | } 193 | } 194 | 195 | // ForEachMutableIf implements the Map interface. 196 | func (sl *SkipList[K, V]) ForEachMutableIf(op func(K, *V) bool) { 197 | for e := sl.head.next[0]; e != nil; e = e.next[0] { 198 | if !op(e.key, &e.value) { 199 | return 200 | } 201 | } 202 | } 203 | 204 | /// SkipList implementation part. 205 | 206 | type skipListNode[K any, V any] struct { 207 | key K 208 | value V 209 | next []*skipListNode[K, V] 210 | } 211 | 212 | //go:generate bash ./skiplist_newnode_generate.sh skipListMaxLevel skiplist_newnode.go 213 | // func newSkipListNode[K Ordered, V any](level int, key K, value V) *skipListNode[K, V] 214 | 215 | type skipListIterator[K any, V any] struct { 216 | node, end *skipListNode[K, V] 217 | } 218 | 219 | func (it *skipListIterator[K, V]) IsNotEnd() bool { 220 | return it.node != it.end 221 | } 222 | 223 | func (it *skipListIterator[K, V]) MoveToNext() { 224 | it.node = it.node.next[0] 225 | } 226 | 227 | func (it *skipListIterator[K, V]) Key() K { 228 | return it.node.key 229 | } 230 | 231 | func (it *skipListIterator[K, V]) Value() V { 232 | return it.node.value 233 | } 234 | 235 | func (it *skipListIterator[K, V]) Pointer() *V { 236 | return &it.node.value 237 | } 238 | 239 | // skipListImpl is an interface to provide different implementation for Ordered key or CompareFn. 240 | // 241 | // We can use CompareFn to compare Ordered keys, but a separated implementation is much faster. 242 | // We don't make the whole skip list an interface, in order to share the type independented method. 243 | // And because these methods are called directly without going through the interface, they are also 244 | // much faster. 245 | type skipListImpl[K any, V any] interface { 246 | findNode(key K) *skipListNode[K, V] 247 | lowerBound(key K) *skipListNode[K, V] 248 | upperBound(key K) *skipListNode[K, V] 249 | findInsertPoint(key K) (*skipListNode[K, V], []*skipListNode[K, V]) 250 | findRemovePoint(key K) (*skipListNode[K, V], []*skipListNode[K, V]) 251 | } 252 | 253 | func (sl *SkipList[K, V]) init() { 254 | sl.level = 1 255 | // #nosec G404 -- This is not a security condition 256 | sl.rander = rand.New(rand.NewSource(time.Now().Unix())) 257 | sl.prevsCache = make([]*skipListNode[K, V], skipListMaxLevel) 258 | sl.head.next = make([]*skipListNode[K, V], skipListMaxLevel) 259 | } 260 | 261 | func (sl *SkipList[K, V]) randomLevel() int { 262 | total := uint64(1)< 3 && 1<<(level-3) > sl.len { 268 | level-- 269 | } 270 | 271 | return level 272 | } 273 | 274 | /// skipListOrdered part 275 | 276 | // skipListOrdered is the skip list implementation for Ordered types. 277 | type skipListOrdered[K Ordered, V any] struct { 278 | SkipList[K, V] 279 | } 280 | 281 | func (sl *skipListOrdered[K, V]) findNode(key K) *skipListNode[K, V] { 282 | return sl.doFindNode(key, true) 283 | } 284 | 285 | func (sl *skipListOrdered[K, V]) doFindNode(key K, eq bool) *skipListNode[K, V] { 286 | // This function execute the job of findNode if eq is true, otherwise lowBound. 287 | // Passing the control variable eq is ugly but it's faster than testing node 288 | // again outside the function in findNode. 289 | prev := &sl.head 290 | for i := sl.level - 1; i >= 0; i-- { 291 | for cur := prev.next[i]; cur != nil; cur = cur.next[i] { 292 | if cur.key == key { 293 | return cur 294 | } 295 | if cur.key > key { 296 | // All other node in this level must be greater than the key, 297 | // search the next level. 298 | break 299 | } 300 | prev = cur 301 | } 302 | } 303 | if eq { 304 | return nil 305 | } 306 | return prev.next[0] 307 | } 308 | 309 | func (sl *skipListOrdered[K, V]) lowerBound(key K) *skipListNode[K, V] { 310 | return sl.doFindNode(key, false) 311 | } 312 | 313 | func (sl *skipListOrdered[K, V]) upperBound(key K) *skipListNode[K, V] { 314 | node := sl.lowerBound(key) 315 | if node != nil && node.key == key { 316 | return node.next[0] 317 | } 318 | return node 319 | } 320 | 321 | // findInsertPoint returns (*node, nil) to the existed node if the key exists, 322 | // or (nil, []*node) to the previous nodes if the key doesn't exist 323 | func (sl *skipListOrdered[K, V]) findInsertPoint(key K) (*skipListNode[K, V], []*skipListNode[K, V]) { 324 | prevs := sl.prevsCache[0:sl.level] 325 | prev := &sl.head 326 | for i := sl.level - 1; i >= 0; i-- { 327 | for next := prev.next[i]; next != nil; next = next.next[i] { 328 | if next.key == key { 329 | // The key is already existed, prevs are useless because no new node insertion. 330 | // stop searching. 331 | return next, nil 332 | } 333 | if next.key > key { 334 | // All other node in this level must be greater than the key, 335 | // search the next level. 336 | break 337 | } 338 | prev = next 339 | } 340 | prevs[i] = prev 341 | } 342 | return nil, prevs 343 | } 344 | 345 | // findRemovePoint finds the node which match the key and it's previous nodes. 346 | func (sl *skipListOrdered[K, V]) findRemovePoint(key K) (*skipListNode[K, V], []*skipListNode[K, V]) { 347 | prevs := sl.findPrevNodes(key) 348 | node := prevs[0].next[0] 349 | if node == nil || node.key != key { 350 | return nil, nil 351 | } 352 | return node, prevs 353 | } 354 | 355 | func (sl *skipListOrdered[K, V]) findPrevNodes(key K) []*skipListNode[K, V] { 356 | prevs := sl.prevsCache[0:sl.level] 357 | prev := &sl.head 358 | for i := sl.level - 1; i >= 0; i-- { 359 | for next := prev.next[i]; next != nil; next = next.next[i] { 360 | if next.key >= key { 361 | break 362 | } 363 | prev = next 364 | } 365 | prevs[i] = prev 366 | } 367 | return prevs 368 | } 369 | 370 | /// skipListFunc part 371 | 372 | // skipListFunc is the skip list implementation which compare keys with func. 373 | type skipListFunc[K any, V any] struct { 374 | SkipList[K, V] 375 | keyCmp CompareFn[K] 376 | } 377 | 378 | func (sl *skipListFunc[K, V]) findNode(key K) *skipListNode[K, V] { 379 | node := sl.lowerBound(key) 380 | if node != nil && sl.keyCmp(node.key, key) == 0 { 381 | return node 382 | } 383 | return nil 384 | } 385 | 386 | func (sl *skipListFunc[K, V]) lowerBound(key K) *skipListNode[K, V] { 387 | var prev = &sl.head 388 | for i := sl.level - 1; i >= 0; i-- { 389 | cur := prev.next[i] 390 | for ; cur != nil; cur = cur.next[i] { 391 | cmpRet := sl.keyCmp(cur.key, key) 392 | if cmpRet == 0 { 393 | return cur 394 | } 395 | if cmpRet > 0 { 396 | break 397 | } 398 | prev = cur 399 | } 400 | } 401 | return prev.next[0] 402 | } 403 | 404 | func (sl *skipListFunc[K, V]) upperBound(key K) *skipListNode[K, V] { 405 | node := sl.lowerBound(key) 406 | if node != nil && sl.keyCmp(node.key, key) == 0 { 407 | return node.next[0] 408 | } 409 | return node 410 | } 411 | 412 | // findInsertPoint returns (*node, nil) to the existed node if the key exists, 413 | // or (nil, []*node) to the previous nodes if the key doesn't exist 414 | func (sl *skipListFunc[K, V]) findInsertPoint(key K) (*skipListNode[K, V], []*skipListNode[K, V]) { 415 | prevs := sl.prevsCache[0:sl.level] 416 | prev := &sl.head 417 | for i := sl.level - 1; i >= 0; i-- { 418 | for cur := prev.next[i]; cur != nil; cur = cur.next[i] { 419 | r := sl.keyCmp(cur.key, key) 420 | if r == 0 { 421 | // The key is already existed, prevs are useless because no new node insertion. 422 | // stop searching. 423 | return cur, nil 424 | } 425 | if r > 0 { 426 | // All other node in this level must be greater than the key, 427 | // search the next level. 428 | break 429 | } 430 | prev = cur 431 | } 432 | prevs[i] = prev 433 | } 434 | return nil, prevs 435 | } 436 | 437 | // findRemovePoint finds the node which match the key and it's previous nodes. 438 | func (sl *skipListFunc[K, V]) findRemovePoint(key K) (*skipListNode[K, V], []*skipListNode[K, V]) { 439 | prevs := sl.findPrevNodes(key) 440 | node := prevs[0].next[0] 441 | if node == nil || sl.keyCmp(node.key, key) != 0 { 442 | return nil, nil 443 | } 444 | return node, prevs 445 | } 446 | 447 | func (sl *skipListFunc[K, V]) findPrevNodes(key K) []*skipListNode[K, V] { 448 | prevs := sl.prevsCache[0:sl.level] 449 | prev := &sl.head 450 | for i := sl.level - 1; i >= 0; i-- { 451 | for next := prev.next[i]; next != nil; next = next.next[i] { 452 | if sl.keyCmp(next.key, key) >= 0 { 453 | break 454 | } 455 | prev = next 456 | } 457 | prevs[i] = prev 458 | } 459 | return prevs 460 | } 461 | -------------------------------------------------------------------------------- /skiplist.md: -------------------------------------------------------------------------------- 1 | # SkipList 2 | 3 | TODO,See tests. 4 | -------------------------------------------------------------------------------- /skiplist_bench_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | ) 7 | 8 | const ( 9 | benchInitSize = 1000000 10 | benchBatchSize = 10 11 | ) 12 | 13 | func newMapN(n int) map[int]int { 14 | m := map[int]int{} 15 | for i := 0; i < n; i++ { 16 | m[i] = i 17 | } 18 | return m 19 | } 20 | 21 | func BenchmarkSkipList_Iterate(b *testing.B) { 22 | sl := newSkipListN(100) 23 | b.ResetTimer() 24 | for n := 0; n < b.N; n++ { 25 | for i := sl.Iterate(); i.IsNotEnd(); i.MoveToNext() { 26 | _, _ = i.Key(), i.Value() 27 | } 28 | } 29 | } 30 | 31 | func BenchmarkSkipList_Insert(b *testing.B) { 32 | start := benchInitSize 33 | sl := newSkipListN(start) 34 | b.ResetTimer() 35 | for n := 0; n < b.N; n++ { 36 | for i := 0; i < benchBatchSize; i++ { 37 | sl.Insert(start+i, i) 38 | } 39 | start += benchBatchSize 40 | } 41 | } 42 | 43 | func BenchmarkMap_Insert(b *testing.B) { 44 | start := benchInitSize 45 | m := newMapN(start) 46 | b.ResetTimer() 47 | for n := 0; n < b.N; n++ { 48 | for i := 0; i < benchBatchSize; i++ { 49 | m[start+i] = i 50 | } 51 | start += benchBatchSize 52 | } 53 | } 54 | 55 | func BenchmarkSkipList_Insert_Dup(b *testing.B) { 56 | sl := newSkipListN(benchInitSize) 57 | b.ResetTimer() 58 | for n := 0; n < b.N; n++ { 59 | for i := 0; i < benchBatchSize; i++ { 60 | sl.Insert(i, i) 61 | } 62 | } 63 | } 64 | 65 | func BenchmarkMap_Insert_Dup(b *testing.B) { 66 | m := newMapN(benchInitSize) 67 | b.ResetTimer() 68 | for n := 0; n < b.N; n++ { 69 | for i := 0; i < benchBatchSize; i++ { 70 | m[i] = i 71 | } 72 | } 73 | } 74 | 75 | func BenchmarkMap_Find(b *testing.B) { 76 | m := newMapN(benchInitSize) 77 | b.ResetTimer() 78 | for n := 0; n < b.N; n++ { 79 | for i := 0; i < benchBatchSize; i++ { 80 | _, _ = m[i] 81 | } 82 | } 83 | } 84 | 85 | func BenchmarkSkipList_Find(b *testing.B) { 86 | sl := newSkipListN(benchInitSize) 87 | b.ResetTimer() 88 | b.Run("Find", func(b *testing.B) { 89 | for i := 0; i < b.N; i++ { 90 | for n := 0; n < benchBatchSize; n++ { 91 | _ = sl.Find(n) 92 | } 93 | } 94 | }) 95 | b.Run("LowerBound", func(b *testing.B) { 96 | for i := 0; i < b.N; i++ { 97 | for n := 0; n < benchBatchSize; n++ { 98 | _ = sl.impl.lowerBound(n) 99 | } 100 | } 101 | }) 102 | b.Run("FindEnd", func(b *testing.B) { 103 | for i := 0; i < b.N; i++ { 104 | for n := 0; n < benchBatchSize; n++ { 105 | _ = sl.Find(benchInitSize) 106 | } 107 | } 108 | }) 109 | } 110 | 111 | func BenchmarkSkipListString(b *testing.B) { 112 | sl := NewSkipList[string, int]() 113 | sl.rander.Seed(0) 114 | var a []string 115 | for i := 0; i < benchBatchSize; i++ { 116 | a = append(a, strconv.Itoa(benchInitSize+i)) 117 | } 118 | end := strconv.Itoa(2 * benchInitSize) 119 | b.ResetTimer() 120 | b.Run("Insert", func(b *testing.B) { 121 | for i := 0; i < b.N; i++ { 122 | for n := 0; n < benchBatchSize; n++ { 123 | sl.Insert(a[n], n) 124 | } 125 | } 126 | }) 127 | b.Run("Find", func(b *testing.B) { 128 | for i := 0; i < b.N; i++ { 129 | for n := 0; n < benchBatchSize; n++ { 130 | sl.Find(a[n]) 131 | } 132 | } 133 | }) 134 | b.Run("FindEnd", func(b *testing.B) { 135 | for i := 0; i < b.N; i++ { 136 | for n := 0; n < benchBatchSize; n++ { 137 | sl.Find(end) 138 | } 139 | } 140 | }) 141 | 142 | b.Run("RemoveEnd", func(b *testing.B) { 143 | for i := 0; i < b.N; i++ { 144 | for n := 0; n < benchBatchSize; n++ { 145 | sl.Remove(end) 146 | } 147 | } 148 | }) 149 | b.Run("Remove", func(b *testing.B) { 150 | for i := 0; i < b.N; i++ { 151 | for n := 0; n < benchBatchSize; n++ { 152 | sl.Remove(a[n]) 153 | } 154 | } 155 | }) 156 | } 157 | -------------------------------------------------------------------------------- /skiplist_newnode.go: -------------------------------------------------------------------------------- 1 | // AUTO GENERATED CODE, DO NOT EDIT!!! 2 | // EDIT skiplist_newnode_generate.sh accordingly. 3 | 4 | package stl4go 5 | 6 | // newSkipListNode creates a new node initialized with specified key, value and next slice. 7 | func newSkipListNode[K any, V any](level int, key K, value V) *skipListNode[K, V] { 8 | // For nodes with each levels, point their next slice to the nexts array allocated together, 9 | // which can reduce 1 memory allocation and improve performance. 10 | // 11 | // The generics of the golang doesn't support non-type parameters like in C++, 12 | // so we have to generate it manually. 13 | switch level { 14 | case 1: 15 | n := struct { 16 | head skipListNode[K, V] 17 | nexts [1]*skipListNode[K, V] 18 | }{head: skipListNode[K, V]{key, value, nil}} 19 | n.head.next = n.nexts[:] 20 | return &n.head 21 | case 2: 22 | n := struct { 23 | head skipListNode[K, V] 24 | nexts [2]*skipListNode[K, V] 25 | }{head: skipListNode[K, V]{key, value, nil}} 26 | n.head.next = n.nexts[:] 27 | return &n.head 28 | case 3: 29 | n := struct { 30 | head skipListNode[K, V] 31 | nexts [3]*skipListNode[K, V] 32 | }{head: skipListNode[K, V]{key, value, nil}} 33 | n.head.next = n.nexts[:] 34 | return &n.head 35 | case 4: 36 | n := struct { 37 | head skipListNode[K, V] 38 | nexts [4]*skipListNode[K, V] 39 | }{head: skipListNode[K, V]{key, value, nil}} 40 | n.head.next = n.nexts[:] 41 | return &n.head 42 | case 5: 43 | n := struct { 44 | head skipListNode[K, V] 45 | nexts [5]*skipListNode[K, V] 46 | }{head: skipListNode[K, V]{key, value, nil}} 47 | n.head.next = n.nexts[:] 48 | return &n.head 49 | case 6: 50 | n := struct { 51 | head skipListNode[K, V] 52 | nexts [6]*skipListNode[K, V] 53 | }{head: skipListNode[K, V]{key, value, nil}} 54 | n.head.next = n.nexts[:] 55 | return &n.head 56 | case 7: 57 | n := struct { 58 | head skipListNode[K, V] 59 | nexts [7]*skipListNode[K, V] 60 | }{head: skipListNode[K, V]{key, value, nil}} 61 | n.head.next = n.nexts[:] 62 | return &n.head 63 | case 8: 64 | n := struct { 65 | head skipListNode[K, V] 66 | nexts [8]*skipListNode[K, V] 67 | }{head: skipListNode[K, V]{key, value, nil}} 68 | n.head.next = n.nexts[:] 69 | return &n.head 70 | case 9: 71 | n := struct { 72 | head skipListNode[K, V] 73 | nexts [9]*skipListNode[K, V] 74 | }{head: skipListNode[K, V]{key, value, nil}} 75 | n.head.next = n.nexts[:] 76 | return &n.head 77 | case 10: 78 | n := struct { 79 | head skipListNode[K, V] 80 | nexts [10]*skipListNode[K, V] 81 | }{head: skipListNode[K, V]{key, value, nil}} 82 | n.head.next = n.nexts[:] 83 | return &n.head 84 | case 11: 85 | n := struct { 86 | head skipListNode[K, V] 87 | nexts [11]*skipListNode[K, V] 88 | }{head: skipListNode[K, V]{key, value, nil}} 89 | n.head.next = n.nexts[:] 90 | return &n.head 91 | case 12: 92 | n := struct { 93 | head skipListNode[K, V] 94 | nexts [12]*skipListNode[K, V] 95 | }{head: skipListNode[K, V]{key, value, nil}} 96 | n.head.next = n.nexts[:] 97 | return &n.head 98 | case 13: 99 | n := struct { 100 | head skipListNode[K, V] 101 | nexts [13]*skipListNode[K, V] 102 | }{head: skipListNode[K, V]{key, value, nil}} 103 | n.head.next = n.nexts[:] 104 | return &n.head 105 | case 14: 106 | n := struct { 107 | head skipListNode[K, V] 108 | nexts [14]*skipListNode[K, V] 109 | }{head: skipListNode[K, V]{key, value, nil}} 110 | n.head.next = n.nexts[:] 111 | return &n.head 112 | case 15: 113 | n := struct { 114 | head skipListNode[K, V] 115 | nexts [15]*skipListNode[K, V] 116 | }{head: skipListNode[K, V]{key, value, nil}} 117 | n.head.next = n.nexts[:] 118 | return &n.head 119 | case 16: 120 | n := struct { 121 | head skipListNode[K, V] 122 | nexts [16]*skipListNode[K, V] 123 | }{head: skipListNode[K, V]{key, value, nil}} 124 | n.head.next = n.nexts[:] 125 | return &n.head 126 | case 17: 127 | n := struct { 128 | head skipListNode[K, V] 129 | nexts [17]*skipListNode[K, V] 130 | }{head: skipListNode[K, V]{key, value, nil}} 131 | n.head.next = n.nexts[:] 132 | return &n.head 133 | case 18: 134 | n := struct { 135 | head skipListNode[K, V] 136 | nexts [18]*skipListNode[K, V] 137 | }{head: skipListNode[K, V]{key, value, nil}} 138 | n.head.next = n.nexts[:] 139 | return &n.head 140 | case 19: 141 | n := struct { 142 | head skipListNode[K, V] 143 | nexts [19]*skipListNode[K, V] 144 | }{head: skipListNode[K, V]{key, value, nil}} 145 | n.head.next = n.nexts[:] 146 | return &n.head 147 | case 20: 148 | n := struct { 149 | head skipListNode[K, V] 150 | nexts [20]*skipListNode[K, V] 151 | }{head: skipListNode[K, V]{key, value, nil}} 152 | n.head.next = n.nexts[:] 153 | return &n.head 154 | case 21: 155 | n := struct { 156 | head skipListNode[K, V] 157 | nexts [21]*skipListNode[K, V] 158 | }{head: skipListNode[K, V]{key, value, nil}} 159 | n.head.next = n.nexts[:] 160 | return &n.head 161 | case 22: 162 | n := struct { 163 | head skipListNode[K, V] 164 | nexts [22]*skipListNode[K, V] 165 | }{head: skipListNode[K, V]{key, value, nil}} 166 | n.head.next = n.nexts[:] 167 | return &n.head 168 | case 23: 169 | n := struct { 170 | head skipListNode[K, V] 171 | nexts [23]*skipListNode[K, V] 172 | }{head: skipListNode[K, V]{key, value, nil}} 173 | n.head.next = n.nexts[:] 174 | return &n.head 175 | case 24: 176 | n := struct { 177 | head skipListNode[K, V] 178 | nexts [24]*skipListNode[K, V] 179 | }{head: skipListNode[K, V]{key, value, nil}} 180 | n.head.next = n.nexts[:] 181 | return &n.head 182 | case 25: 183 | n := struct { 184 | head skipListNode[K, V] 185 | nexts [25]*skipListNode[K, V] 186 | }{head: skipListNode[K, V]{key, value, nil}} 187 | n.head.next = n.nexts[:] 188 | return &n.head 189 | case 26: 190 | n := struct { 191 | head skipListNode[K, V] 192 | nexts [26]*skipListNode[K, V] 193 | }{head: skipListNode[K, V]{key, value, nil}} 194 | n.head.next = n.nexts[:] 195 | return &n.head 196 | case 27: 197 | n := struct { 198 | head skipListNode[K, V] 199 | nexts [27]*skipListNode[K, V] 200 | }{head: skipListNode[K, V]{key, value, nil}} 201 | n.head.next = n.nexts[:] 202 | return &n.head 203 | case 28: 204 | n := struct { 205 | head skipListNode[K, V] 206 | nexts [28]*skipListNode[K, V] 207 | }{head: skipListNode[K, V]{key, value, nil}} 208 | n.head.next = n.nexts[:] 209 | return &n.head 210 | case 29: 211 | n := struct { 212 | head skipListNode[K, V] 213 | nexts [29]*skipListNode[K, V] 214 | }{head: skipListNode[K, V]{key, value, nil}} 215 | n.head.next = n.nexts[:] 216 | return &n.head 217 | case 30: 218 | n := struct { 219 | head skipListNode[K, V] 220 | nexts [30]*skipListNode[K, V] 221 | }{head: skipListNode[K, V]{key, value, nil}} 222 | n.head.next = n.nexts[:] 223 | return &n.head 224 | case 31: 225 | n := struct { 226 | head skipListNode[K, V] 227 | nexts [31]*skipListNode[K, V] 228 | }{head: skipListNode[K, V]{key, value, nil}} 229 | n.head.next = n.nexts[:] 230 | return &n.head 231 | case 32: 232 | n := struct { 233 | head skipListNode[K, V] 234 | nexts [32]*skipListNode[K, V] 235 | }{head: skipListNode[K, V]{key, value, nil}} 236 | n.head.next = n.nexts[:] 237 | return &n.head 238 | case 33: 239 | n := struct { 240 | head skipListNode[K, V] 241 | nexts [33]*skipListNode[K, V] 242 | }{head: skipListNode[K, V]{key, value, nil}} 243 | n.head.next = n.nexts[:] 244 | return &n.head 245 | case 34: 246 | n := struct { 247 | head skipListNode[K, V] 248 | nexts [34]*skipListNode[K, V] 249 | }{head: skipListNode[K, V]{key, value, nil}} 250 | n.head.next = n.nexts[:] 251 | return &n.head 252 | case 35: 253 | n := struct { 254 | head skipListNode[K, V] 255 | nexts [35]*skipListNode[K, V] 256 | }{head: skipListNode[K, V]{key, value, nil}} 257 | n.head.next = n.nexts[:] 258 | return &n.head 259 | case 36: 260 | n := struct { 261 | head skipListNode[K, V] 262 | nexts [36]*skipListNode[K, V] 263 | }{head: skipListNode[K, V]{key, value, nil}} 264 | n.head.next = n.nexts[:] 265 | return &n.head 266 | case 37: 267 | n := struct { 268 | head skipListNode[K, V] 269 | nexts [37]*skipListNode[K, V] 270 | }{head: skipListNode[K, V]{key, value, nil}} 271 | n.head.next = n.nexts[:] 272 | return &n.head 273 | case 38: 274 | n := struct { 275 | head skipListNode[K, V] 276 | nexts [38]*skipListNode[K, V] 277 | }{head: skipListNode[K, V]{key, value, nil}} 278 | n.head.next = n.nexts[:] 279 | return &n.head 280 | case 39: 281 | n := struct { 282 | head skipListNode[K, V] 283 | nexts [39]*skipListNode[K, V] 284 | }{head: skipListNode[K, V]{key, value, nil}} 285 | n.head.next = n.nexts[:] 286 | return &n.head 287 | case 40: 288 | n := struct { 289 | head skipListNode[K, V] 290 | nexts [40]*skipListNode[K, V] 291 | }{head: skipListNode[K, V]{key, value, nil}} 292 | n.head.next = n.nexts[:] 293 | return &n.head 294 | } 295 | 296 | panic("should not reach here") 297 | } 298 | -------------------------------------------------------------------------------- /skiplist_newnode_generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # The go generics sucks! 4 | # We have to generate skiplist_newnode.go by myself. 5 | 6 | generate() { 7 | # NOTE the tab chars, they are used to follow the go style guides. 8 | echo "package $GOPACKAGE" 9 | echo " 10 | // newSkipListNode creates a new node initialized with specified key, value and next slice. 11 | func newSkipListNode[K any, V any](level int, key K, value V) *skipListNode[K, V] { 12 | // For nodes with each levels, point their next slice to the nexts array allocated together, 13 | // which can reduce 1 memory allocation and improve performance. 14 | // 15 | // The generics of the golang doesn't support non-type parameters like in C++, 16 | // so we have to generate it manually. 17 | switch level {" 18 | 19 | for i in $(seq 1 $1); do 20 | echo "\ 21 | case $i: 22 | n := struct { 23 | head skipListNode[K, V] 24 | nexts [$i]*skipListNode[K, V] 25 | }{head: skipListNode[K, V]{key, value, nil}} 26 | n.head.next = n.nexts[:] 27 | return &n.head" 28 | done 29 | echo "\ 30 | } 31 | 32 | panic(\"should not reach here\") 33 | }" 34 | } 35 | 36 | max_level=$(grep -E "^\s*$1\s*=\s*\d+" $GOFILE | grep -Eo "\d+") 37 | output_file=$2 38 | 39 | echo -e "// AUTO GENERATED CODE, DO NOT EDIT!!!\n// EDIT $(basename $0) accordingly.\n" > $output_file 40 | generate $max_level >> $output_file 41 | -------------------------------------------------------------------------------- /skiplist_set.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | // SkipListSet is a SortedSet implemented with skiplist. 4 | type SkipListSet[K any] SkipList[K, struct{}] 5 | 6 | // NewSkipListSet creates a new SkipListSet object. 7 | func NewSkipListSet[K Ordered]() *SkipListSet[K] { 8 | return (*SkipListSet[K])(NewSkipList[K, struct{}]()) 9 | } 10 | 11 | // NewSkipListSetFunc creates a new SkipListSet object with specified compare function. 12 | func NewSkipListSetFunc[K any](cmp CompareFn[K]) *SkipListSet[K] { 13 | return (*SkipListSet[K])(NewSkipListFunc[K, struct{}](cmp)) 14 | } 15 | 16 | // NewSkipListSetOf creates a new SkipListSet object with specified elements. 17 | func NewSkipListSetOf[K Ordered](elements ...K) *SkipListSet[K] { 18 | s := NewSkipListSet[K]() 19 | for i := range elements { 20 | s.Insert(elements[i]) 21 | } 22 | return s 23 | } 24 | 25 | // IsEmpty implements the SortedSet interface. 26 | func (s *SkipListSet[K]) IsEmpty() bool { 27 | return s.asMap().IsEmpty() 28 | } 29 | 30 | // Len implements the SortedSet interface. 31 | func (s *SkipListSet[K]) Len() int { 32 | return s.asMap().Len() 33 | } 34 | 35 | // Clear implements the SortedSet interface. 36 | func (s *SkipListSet[K]) Clear() { 37 | s.asMap().Clear() 38 | } 39 | 40 | // Has implements the SortedSet interface. 41 | func (s *SkipListSet[K]) Has(key K) bool { 42 | return s.asMap().Has(key) 43 | } 44 | 45 | // Insert implements the SortedSet interface. 46 | func (s *SkipListSet[K]) Insert(key K) bool { 47 | oldLen := s.Len() 48 | s.asMap().Insert(key, struct{}{}) 49 | return s.Len() > oldLen 50 | } 51 | 52 | // InsertN implements the SortedSet interface. 53 | func (s *SkipListSet[K]) InsertN(keys ...K) int { 54 | oldLen := s.Len() 55 | for i := range keys { 56 | s.Insert(keys[i]) 57 | } 58 | return s.Len() - oldLen 59 | } 60 | 61 | // Remove implements the SortedSet interface. 62 | func (s *SkipListSet[K]) Remove(key K) bool { 63 | return s.asMap().Remove(key) 64 | } 65 | 66 | // RemoveN implements the SortedSet interface. 67 | func (s *SkipListSet[K]) RemoveN(keys ...K) int { 68 | oldLen := s.Len() 69 | for i := range keys { 70 | s.Remove(keys[i]) 71 | } 72 | return oldLen - s.Len() 73 | } 74 | 75 | // Keys return a copy of sorted keys as slice. 76 | func (s *SkipListSet[K]) Keys() []K { 77 | keys := make([]K, 0, s.Len()) 78 | s.ForEach(func(k K) { keys = append(keys, k) }) 79 | return keys 80 | } 81 | 82 | // ForEach implements the SortedSet interface. 83 | func (s *SkipListSet[K]) ForEach(f func(K)) { 84 | s.asMap().ForEach(func(k K, s struct{}) { f(k) }) 85 | } 86 | 87 | // ForEachIf implements the SortedSet interface. 88 | func (s *SkipListSet[K]) ForEachIf(f func(K) bool) { 89 | s.asMap().ForEachIf(func(k K, s struct{}) bool { return f(k) }) 90 | } 91 | 92 | // LowerBound implements the SortedSet interface. 93 | func (s *SkipListSet[K]) LowerBound(key K) Iterator[K] { 94 | return &skipListSetIterator[K]{s.asMap().impl.lowerBound(key), nil} 95 | } 96 | 97 | // UpperBound implements the SortedSet interface. 98 | func (s *SkipListSet[K]) UpperBound(key K) Iterator[K] { 99 | return &skipListSetIterator[K]{s.asMap().impl.upperBound(key), nil} 100 | } 101 | 102 | // FindRange implements the SortedSet interface. 103 | func (s *SkipListSet[K]) FindRange(first, last K) Iterator[K] { 104 | m := s.asMap().impl 105 | return &skipListSetIterator[K]{m.lowerBound(first), m.upperBound(last)} 106 | } 107 | 108 | func (s *SkipListSet[K]) asMap() *SkipList[K, struct{}] { 109 | return (*SkipList[K, struct{}])(s) 110 | } 111 | 112 | type skipListSetIterator[K any] skipListIterator[K, struct{}] 113 | 114 | func (it *skipListSetIterator[K]) IsNotEnd() bool { 115 | return it.node != it.end 116 | } 117 | 118 | func (it *skipListSetIterator[K]) MoveToNext() { 119 | it.node = it.node.next[0] 120 | } 121 | 122 | func (it *skipListSetIterator[K]) Value() K { 123 | return it.node.key 124 | } 125 | -------------------------------------------------------------------------------- /skiplist_set_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestNewSkipList_Interface(t *testing.T) { 8 | s := NewSkipListSet[int]() 9 | _ = SortedSet[int](s) 10 | } 11 | 12 | func TestNewSkipListSet(t *testing.T) { 13 | s := NewSkipListSet[int]() 14 | expectTrue(t, s.IsEmpty()) 15 | expectEq(t, s.Len(), 0) 16 | } 17 | 18 | func TestNewSkipListSetOf(t *testing.T) { 19 | s := NewSkipListSetOf(1, 2, 3, 4, 5) 20 | expectTrue(t, Equal(s.Keys(), []int{1, 2, 3, 4, 5})) 21 | } 22 | 23 | func TestNewSkipListSetFunc(t *testing.T) { 24 | s := NewSkipListSetFunc(OrderedCompare[int]) 25 | s.InsertN(1, 2, 3, 4, 5) 26 | expectTrue(t, Equal(s.Keys(), []int{1, 2, 3, 4, 5})) 27 | } 28 | 29 | func TestNewSkipListSet_Insert_Remove_Has(t *testing.T) { 30 | s := NewSkipListSet[int]() 31 | 32 | expectFalse(t, s.Remove(1)) 33 | expectFalse(t, s.Has(1)) 34 | 35 | expectTrue(t, s.Insert(1)) 36 | expectFalse(t, s.Insert(1)) 37 | expectTrue(t, s.Has(1)) 38 | expectFalse(t, s.IsEmpty()) 39 | expectEq(t, s.Len(), 1) 40 | 41 | expectTrue(t, s.Remove(1)) 42 | expectFalse(t, s.Remove(1)) 43 | expectFalse(t, s.Has(1)) 44 | 45 | expectTrue(t, s.IsEmpty()) 46 | expectEq(t, s.Len(), 0) 47 | } 48 | 49 | func TestNewSkipListSet_Clear(t *testing.T) { 50 | s := NewSkipListSetOf(1, 2, 3, 4, 5) 51 | s.Clear() 52 | expectTrue(t, s.IsEmpty()) 53 | expectEq(t, s.Len(), 0) 54 | } 55 | 56 | func TestNewSkipListSet_InsertN(t *testing.T) { 57 | s := NewSkipListSet[int]() 58 | expectEq(t, s.InsertN(1, 2, 3, 1), 3) 59 | expectFalse(t, s.IsEmpty()) 60 | expectEq(t, s.Len(), 3) 61 | } 62 | 63 | func TestNewSkipListSet_RemoveN(t *testing.T) { 64 | s := NewSkipListSetOf(1, 2, 3, 4) 65 | expectEq(t, s.RemoveN(2, 4, 6), 2) 66 | expectFalse(t, s.IsEmpty()) 67 | expectEq(t, s.Len(), 2) 68 | expectTrue(t, Equal(s.Keys(), []int{1, 3})) 69 | } 70 | 71 | func TestNewSkipListSet_ForEach(t *testing.T) { 72 | s := NewSkipListSetOf(1, 2, 3, 4) 73 | var a []int 74 | s.ForEach(func(i int) { a = append(a, i) }) 75 | expectTrue(t, Equal(a, []int{1, 2, 3, 4})) 76 | } 77 | 78 | func TestNewSkipListSet_ForEachIf(t *testing.T) { 79 | s := NewSkipListSetOf(1, 2, 3, 4) 80 | var a []int 81 | s.ForEachIf(func(i int) bool { a = append(a, i); return i < 2 }) 82 | expectTrue(t, Equal(a, []int{1, 2})) 83 | } 84 | 85 | func TestNewSkipList_Iterate(t *testing.T) { 86 | sl := newSkipListN(10) 87 | i := 0 88 | for it := sl.Iterate(); it.IsNotEnd(); it.MoveToNext() { 89 | expectEq(t, it.Key(), i) 90 | expectEq(t, it.Value(), i) 91 | expectEq(t, *it.Pointer(), i) 92 | i++ 93 | } 94 | } 95 | 96 | func TestSkipListSet_Iterater(t *testing.T) { 97 | s := NewSkipListSetOf(1, 2, 4) 98 | t.Run("LowerBound", func(t *testing.T) { 99 | expectEq(t, s.LowerBound(1).Value(), 1) 100 | expectEq(t, s.LowerBound(3).Value(), 4) 101 | expectEq(t, s.LowerBound(4).Value(), 4) 102 | expectFalse(t, s.LowerBound(5).IsNotEnd()) 103 | }) 104 | 105 | t.Run("UpperBound", func(t *testing.T) { 106 | expectEq(t, s.UpperBound(0).Value(), 1) 107 | expectEq(t, s.UpperBound(1).Value(), 2) 108 | expectEq(t, s.UpperBound(3).Value(), 4) 109 | expectFalse(t, s.UpperBound(4).IsNotEnd()) 110 | expectFalse(t, s.UpperBound(5).IsNotEnd()) 111 | }) 112 | 113 | t.Run("FindRange", func(t *testing.T) { 114 | it := s.FindRange(1, 3) 115 | expectEq(t, it.Value(), 1) 116 | it.MoveToNext() 117 | expectEq(t, it.Value(), 2) 118 | }) 119 | } 120 | -------------------------------------------------------------------------------- /skiplist_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "math" 5 | "testing" 6 | ) 7 | 8 | func TestSkipList_Interface(t *testing.T) { 9 | sl := NewSkipList[int, int]() 10 | _ = Map[int, int](sl) 11 | _ = SortedMap[int, int](sl) 12 | _ = MapIterator[int, int](sl.Iterate()) 13 | _ = MutableMapIterator[int, int](sl.Iterate()) 14 | } 15 | 16 | func TestNewSkipListString(t *testing.T) { 17 | sl := NewSkipList[string, int]() 18 | sl.Insert("hello", 1) 19 | expectTrue(t, sl.Has("hello")) 20 | expectEq(t, *sl.Find("hello"), 1) 21 | expectFalse(t, sl.Has("world")) 22 | expectEq(t, sl.Find("world"), nil) 23 | } 24 | 25 | func testNewSkipListType[T Numeric](t *testing.T) { 26 | sl := NewSkipList[T, int]() 27 | var n T = 1 28 | sl.Insert(n, 1) 29 | expectTrue(t, sl.Has(n)) 30 | expectEq(t, *sl.Find(n), 1) 31 | expectFalse(t, sl.Has(n+1)) 32 | expectEq(t, sl.Find(n+1), nil) 33 | } 34 | 35 | func TestNewSkipListInt8(t *testing.T) { testNewSkipListType[int8](t) } 36 | func TestNewSkipListInt16(t *testing.T) { testNewSkipListType[int16](t) } 37 | func TestNewSkipListInt32(t *testing.T) { testNewSkipListType[int32](t) } 38 | func TestNewSkipListInt64(t *testing.T) { testNewSkipListType[int64](t) } 39 | 40 | func TestNewSkipListUInt8(t *testing.T) { testNewSkipListType[uint8](t) } 41 | func TestNewSkipListUInt16(t *testing.T) { testNewSkipListType[uint16](t) } 42 | func TestNewSkipListUInt32(t *testing.T) { testNewSkipListType[uint32](t) } 43 | func TestNewSkipListUInt64(t *testing.T) { testNewSkipListType[uint64](t) } 44 | 45 | func TestNewSkipListUIntPtr(t *testing.T) { testNewSkipListType[uintptr](t) } 46 | 47 | func TestNewSkipListFloat32(t *testing.T) { testNewSkipListType[float32](t) } 48 | func TestNewSkipListFloat64(t *testing.T) { testNewSkipListType[float64](t) } 49 | 50 | func TestNewSkipListFunc(t *testing.T) { 51 | type Person struct { 52 | name string 53 | age int 54 | } 55 | personCmp := func(a, b Person) int { 56 | r := OrderedCompare(a.age, b.age) 57 | if r != 0 { 58 | return r 59 | } 60 | return OrderedCompare(a.name, b.name) 61 | } 62 | sl := NewSkipListFunc[Person, int](personCmp) 63 | sl.Insert(Person{"zhangsan", 20}, 1) 64 | sl.Insert(Person{"lisi", 20}, 1) 65 | sl.Insert(Person{"wangwu", 30}, 1) 66 | expectTrue(t, sl.Has(Person{"zhangsan", 20})) 67 | expectFalse(t, sl.Has(Person{"zhangsan", 30})) 68 | expectEq(t, sl.Len(), 3) 69 | 70 | sl.Insert(Person{"zhangsan", 20}, 1) 71 | expectEq(t, sl.Len(), 3) 72 | 73 | var ps []Person 74 | sl.ForEach(func(p Person, _ int) { 75 | ps = append(ps, p) 76 | }) 77 | expectEq(t, ps[0].name, "lisi") 78 | expectEq(t, ps[1].name, "zhangsan") 79 | expectEq(t, ps[2].name, "wangwu") 80 | 81 | sl.Remove(Person{"zhangsan", 20}) 82 | expectEq(t, sl.Len(), 2) 83 | 84 | sl.Remove(Person{"zhaoliu", 40}) 85 | expectEq(t, sl.Len(), 2) 86 | } 87 | 88 | func TestNewSkipListFromMap(t *testing.T) { 89 | m := map[int]int{1: -1, 2: -2, 3: -3} 90 | sl := NewSkipListFromMap(m) 91 | for k := range m { 92 | expectTrue(t, sl.Has(k)) 93 | } 94 | } 95 | 96 | func TestSkipList_Iterate(t *testing.T) { 97 | sl := newSkipListN(10) 98 | i := 0 99 | for it := sl.Iterate(); it.IsNotEnd(); it.MoveToNext() { 100 | expectEq(t, it.Key(), i) 101 | expectEq(t, it.Value(), i) 102 | expectEq(t, *it.Pointer(), i) 103 | i++ 104 | } 105 | } 106 | 107 | func testSkipListIterater(t *testing.T, sl *SkipList[int, int]) { 108 | t.Run("LowerBound", func(t *testing.T) { 109 | expectEq(t, sl.LowerBound(1).Key(), 1) 110 | expectEq(t, sl.LowerBound(3).Key(), 4) 111 | expectEq(t, sl.LowerBound(4).Key(), 4) 112 | expectFalse(t, sl.LowerBound(5).IsNotEnd()) 113 | }) 114 | 115 | t.Run("UpperBound", func(t *testing.T) { 116 | expectEq(t, sl.UpperBound(0).Key(), 1) 117 | expectEq(t, sl.UpperBound(1).Key(), 2) 118 | expectEq(t, sl.UpperBound(3).Key(), 4) 119 | expectFalse(t, sl.UpperBound(4).IsNotEnd()) 120 | expectFalse(t, sl.UpperBound(5).IsNotEnd()) 121 | }) 122 | 123 | t.Run("FindRange", func(t *testing.T) { 124 | it := sl.FindRange(1, 3) 125 | expectEq(t, it.Key(), 1) 126 | it.MoveToNext() 127 | expectEq(t, it.Key(), 2) 128 | }) 129 | } 130 | 131 | func TestSkipList_Iterater(t *testing.T) { 132 | sl := NewSkipListFromMap(map[int]int{1: 1, 2: 2, 4: 4}) 133 | testSkipListIterater(t, sl) 134 | } 135 | 136 | func TestSkipList_Func_Iterater(t *testing.T) { 137 | sl := NewSkipListFunc[int, int](OrderedCompare[int]) 138 | m := map[int]int{1: 1, 2: 2, 4: 4} 139 | for k, v := range m { 140 | sl.Insert(k, v) 141 | } 142 | testSkipListIterater(t, sl) 143 | } 144 | func TestSkipList_Insert(t *testing.T) { 145 | sl := NewSkipList[int, int]() 146 | for i := 0; i < 100; i++ { 147 | expectEq(t, sl.Len(), i) 148 | sl.Insert(i, i) 149 | expectEq(t, sl.Len(), i+1) 150 | } 151 | } 152 | 153 | func TestSkipList_Insert_Reverse(t *testing.T) { 154 | sl := NewSkipList[int, int]() 155 | for i := 100; i > 0; i-- { 156 | oldlen := sl.Len() 157 | sl.Insert(i, i) 158 | expectEq(t, sl.Len(), oldlen+1) 159 | } 160 | } 161 | 162 | func TestSkipList_Insert_Dup(t *testing.T) { 163 | sl := NewSkipList[int, int]() 164 | sl.Insert(1, 1) 165 | expectEq(t, sl.Len(), 1) 166 | sl.Insert(1, 2) 167 | expectEq(t, sl.Len(), 1) 168 | } 169 | 170 | func newSkipListN(n int) *SkipList[int, int] { 171 | sl := NewSkipList[int, int]() 172 | sl.rander.Seed(0) 173 | for i := 0; i < n; i++ { 174 | sl.Insert(i, i) 175 | } 176 | return sl 177 | } 178 | 179 | func TestSkipList_Remove(t *testing.T) { 180 | sl := newSkipListN(100) 181 | for i := 0; i < 100; i++ { 182 | sl.Remove(i) 183 | } 184 | expectTrue(t, sl.IsEmpty()) 185 | expectEq(t, sl.Len(), 0) 186 | } 187 | 188 | func TestSkipList_Remove_Nonexist(t *testing.T) { 189 | sl := NewSkipList[int, int]() 190 | sl.Insert(1, 1) 191 | sl.Insert(2, 2) 192 | sl.Remove(0) 193 | sl.Remove(3) 194 | expectEq(t, sl.Len(), 2) 195 | } 196 | 197 | func TestSkipList_Remove_Level(t *testing.T) { 198 | sl := newSkipListN(100) 199 | expectGe(t, sl.level, 4) 200 | for i := 0; i < 100; i++ { 201 | sl.Remove(i) 202 | } 203 | expectEq(t, sl.level, 1) 204 | } 205 | 206 | func TestSkipList_Clean(t *testing.T) { 207 | sl := NewSkipList[int, int]() 208 | for i := 0; i < 100; i++ { 209 | sl.Insert(i, i) 210 | } 211 | sl.Clear() 212 | 213 | expectTrue(t, sl.IsEmpty()) 214 | expectEq(t, sl.Len(), 0) 215 | expectEq(t, sl.level, 1) 216 | } 217 | 218 | func TestSkipList_level(t *testing.T) { 219 | var diffs []int 220 | for i := 0; i < 1000; i++ { 221 | for size := 1; size < 10000; size *= 10 { 222 | sl := newSkipListN(size) 223 | log2Len := int(math.Ceil(math.Log2(float64(sl.Len())))) 224 | diffs = append(diffs, log2Len-sl.level) 225 | } 226 | } 227 | expectLt(t, math.Abs(float64(Average(diffs))), 2) 228 | } 229 | 230 | func TestSkipList_newnode(t *testing.T) { 231 | for level := 1; level <= skipListMaxLevel; level++ { 232 | node := newSkipListNode(level, 1, 1) 233 | expectEq(t, len(node.next), level) 234 | } 235 | expectPanic(t, func() { newSkipListNode(0, 1, 1) }) 236 | expectPanic(t, func() { newSkipListNode(skipListMaxLevel+1, 1, 1) }) 237 | } 238 | 239 | func TestSkipList_Find(t *testing.T) { 240 | sl := newSkipListN(100) 241 | for i := 0; i < 100; i++ { 242 | p := sl.Find(i) 243 | expectEq(t, *p, i) 244 | } 245 | expectEq(t, sl.Find(100), nil) 246 | } 247 | 248 | func TestSkipList_Has(t *testing.T) { 249 | sl := NewSkipList[int, int]() 250 | expectFalse(t, sl.Has(1)) 251 | sl.Insert(1, 2) 252 | expectTrue(t, sl.Has(1)) 253 | } 254 | 255 | func TestSkipList_ForEach(t *testing.T) { 256 | sl := newSkipListN(100) 257 | a := []int{} 258 | sl.ForEach(func(k int, v int) { 259 | a = append(a, k) 260 | }) 261 | expectEq(t, len(a), 100) 262 | expectTrue(t, IsSorted(a)) 263 | } 264 | 265 | func TestSkipList_ForEachIf(t *testing.T) { 266 | sl := newSkipListN(100) 267 | a := []int{} 268 | sl.ForEachIf(func(k int, v int) bool { 269 | if k < 50 { 270 | a = append(a, k) 271 | return true 272 | } 273 | return false 274 | }) 275 | expectLt(t, MaxN(a...), 50) 276 | } 277 | 278 | func TestSkipList_ForEachMutable(t *testing.T) { 279 | sl := newSkipListN(100) 280 | sl.ForEachMutable(func(k int, v *int) { 281 | *v = -*v 282 | }) 283 | for i := 0; i < sl.Len(); i++ { 284 | expectEq(t, *sl.Find(i), -i) 285 | } 286 | } 287 | 288 | func TestSkipList_ForEachMutableIf(t *testing.T) { 289 | sl := newSkipListN(100) 290 | sl.ForEachMutableIf(func(k int, v *int) bool { 291 | if k > 50 { 292 | *v = 0 293 | return false 294 | } 295 | return true 296 | }) 297 | 298 | expectEq(t, *sl.Find(51), 0) 299 | } 300 | -------------------------------------------------------------------------------- /slist.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | // SList is a singly linked list. 4 | type SList[T any] struct { 5 | head *sListNode[T] 6 | tail *sListNode[T] // To support Back and PushBack 7 | length int 8 | } 9 | 10 | type sListNode[T any] struct { 11 | next *sListNode[T] 12 | value T 13 | } 14 | 15 | // SListOf return a SList that contains values. 16 | func SListOf[T any](values ...T) SList[T] { 17 | l := SList[T]{} 18 | for i := range values { 19 | l.PushBack(values[i]) 20 | } 21 | return l 22 | } 23 | 24 | // IsEmpty checks if the container has no elements. 25 | func (l *SList[T]) IsEmpty() bool { 26 | return l.length == 0 27 | } 28 | 29 | // Len returns the number of elements in the container. 30 | func (l *SList[T]) Len() int { 31 | return l.length 32 | } 33 | 34 | // Clear erases all elements from the container. After this call, Len() returns zero. 35 | func (l *SList[T]) Clear() { 36 | l.head = nil 37 | l.tail = nil 38 | l.length = 0 39 | } 40 | 41 | // Front returns the first element in the list. 42 | func (l *SList[T]) Front() T { 43 | if l.IsEmpty() { 44 | panic("!IsEmpty") 45 | } 46 | return l.head.value 47 | } 48 | 49 | // Back returns the last element in the list. 50 | func (l *SList[T]) Back() T { 51 | if l.IsEmpty() { 52 | panic("!IsEmpty") 53 | } 54 | return l.tail.value 55 | } 56 | 57 | // PushFront pushed an element to the front of the list. 58 | func (l *SList[T]) PushFront(v T) { 59 | node := sListNode[T]{l.head, v} 60 | l.head = &node 61 | if l.tail == nil { 62 | l.tail = &node 63 | } 64 | l.length++ 65 | } 66 | 67 | // PushBack pushed an element to the tail of the list. 68 | func (l *SList[T]) PushBack(v T) { 69 | node := sListNode[T]{nil, v} 70 | if l.tail != nil { 71 | l.tail.next = &node 72 | } 73 | l.tail = &node 74 | if l.head == nil { 75 | l.head = &node 76 | } 77 | l.length++ 78 | } 79 | 80 | // PopFront popups an element from the front of the list. 81 | // The list must be non-empty! 82 | func (l *SList[T]) PopFront() T { 83 | if l.IsEmpty() { 84 | panic("!IsEmpty") 85 | } 86 | 87 | node := l.head 88 | l.head = node.next 89 | if l.head == nil { 90 | l.tail = nil 91 | } 92 | l.length-- 93 | return node.value 94 | } 95 | 96 | // Reverse reverses the order of all elements in the container. 97 | func (l *SList[T]) Reverse() { 98 | var head, tail *sListNode[T] 99 | for node := l.head; node != nil; { 100 | next := node.next 101 | node.next = head 102 | head = node 103 | if tail == nil { 104 | tail = node 105 | } 106 | node = next 107 | } 108 | l.head = head 109 | l.tail = tail 110 | } 111 | 112 | // Values copies all elements in the container to a slice and return it. 113 | func (l *SList[T]) Values() []T { 114 | s := make([]T, l.Len()) 115 | for node, i := l.head, 0; node != nil; node = node.next { 116 | s[i] = node.value 117 | i++ 118 | } 119 | return s 120 | } 121 | 122 | // InsertAfter inserts an element after the iterator into the list, 123 | // return an iterator to the inserted element. 124 | // func (l *SList[T]) InsertAfter(it Iterator[T], value T) MutableIterator[T] { 125 | // // cause internal compiler error: panic: runtime error: invalid memory address or nil pointer dereference 126 | // itp := it.(*sListIterator[T]) 127 | // node := itp.node 128 | // newNode := sListNode[T]{node.next, value} 129 | // node.next = &newNode 130 | // return &sListIterator[T]{&newNode} 131 | // } 132 | 133 | // ForEach iterate the list, apply each element to the cb callback function. 134 | func (l *SList[T]) ForEach(cb func(T)) { 135 | for node := l.head; node != nil; node = node.next { 136 | cb(node.value) 137 | } 138 | } 139 | 140 | // ForEachIf iterate the container, apply each element to the cb callback function, 141 | // stop if cb returns false. 142 | func (l *SList[T]) ForEachIf(cb func(T) bool) { 143 | for node := l.head; node != nil; node = node.next { 144 | if !cb(node.value) { 145 | break 146 | } 147 | } 148 | } 149 | 150 | // ForEachMutable iterate the container, apply pointer of each element to the cb callback function. 151 | func (l *SList[T]) ForEachMutable(cb func(*T)) { 152 | for node := l.head; node != nil; node = node.next { 153 | cb(&node.value) 154 | } 155 | } 156 | 157 | // ForEachMutableIf iterate the container, apply pointer of each element to the cb callback function, 158 | // stop if cb returns false. 159 | func (l *SList[T]) ForEachMutableIf(cb func(*T) bool) { 160 | for node := l.head; node != nil; node = node.next { 161 | if !cb(&node.value) { 162 | break 163 | } 164 | } 165 | } 166 | 167 | // Iterate returns an iterator to the whole container. 168 | func (l *SList[T]) Iterate() MutableIterator[T] { 169 | it := sListIterator[T]{l.head} 170 | return &it 171 | } 172 | 173 | type sListIterator[T any] struct { 174 | node *sListNode[T] 175 | } 176 | 177 | func (it *sListIterator[T]) IsNotEnd() bool { 178 | return it.node != nil 179 | } 180 | 181 | func (it *sListIterator[T]) MoveToNext() { 182 | it.node = it.node.next 183 | } 184 | 185 | func (it *sListIterator[T]) Value() T { 186 | return it.node.value 187 | } 188 | 189 | func (it *sListIterator[T]) Pointer() *T { 190 | return &it.node.value 191 | } 192 | 193 | // TODO: Sort 194 | -------------------------------------------------------------------------------- /slist_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_SList_Interface(t *testing.T) { 8 | sl := SList[int]{} 9 | _ = Container(&sl) 10 | } 11 | 12 | func Test_SList_Clean(t *testing.T) { 13 | sl := SList[int]{} 14 | sl.PushFront(1) 15 | sl.Clear() 16 | expectTrue(t, sl.IsEmpty()) 17 | expectEq(t, sl.Len(), 0) 18 | } 19 | 20 | func Test_SList_Front(t *testing.T) { 21 | t.Run("empty", func(t *testing.T) { 22 | sl := SList[int]{} 23 | expectPanic(t, func() { sl.Front() }) 24 | }) 25 | 26 | t.Run("normal", func(t *testing.T) { 27 | sl := SList[int]{} 28 | sl.PushFront(1) 29 | expectEq(t, sl.Front(), 1) 30 | 31 | sl.PushBack(2) 32 | expectEq(t, sl.Front(), 1) 33 | 34 | sl.PushFront(3) 35 | expectEq(t, sl.Front(), 3) 36 | }) 37 | } 38 | 39 | func Test_SList_Back(t *testing.T) { 40 | t.Run("empty", func(t *testing.T) { 41 | sl := SList[int]{} 42 | expectPanic(t, func() { sl.Back() }) 43 | }) 44 | 45 | t.Run("normal", func(t *testing.T) { 46 | sl := SList[int]{} 47 | sl.PushBack(1) 48 | expectEq(t, sl.Back(), 1) 49 | 50 | sl.PushFront(2) 51 | expectEq(t, sl.Back(), 1) 52 | 53 | sl.PushBack(3) 54 | expectEq(t, sl.Back(), 3) 55 | }) 56 | } 57 | 58 | func Test_SList_PushFront(t *testing.T) { 59 | sl := SList[int]{} 60 | for i := 1; i < 10; i++ { 61 | sl.PushFront(i) 62 | expectEq(t, sl.Front(), i) 63 | expectEq(t, sl.Len(), i) 64 | } 65 | } 66 | 67 | func Test_SList_PushBack(t *testing.T) { 68 | sl := SList[int]{} 69 | for i := 1; i < 10; i++ { 70 | sl.PushBack(i) 71 | expectEq(t, sl.Back(), i) 72 | expectEq(t, sl.Len(), i) 73 | expectFalse(t, sl.IsEmpty()) 74 | } 75 | } 76 | 77 | func Test_SList_PopFront(t *testing.T) { 78 | sl := SList[int]{} 79 | expectPanic(t, func() { sl.PopFront() }) 80 | 81 | sl.PushFront(1) 82 | sl.PushFront(2) 83 | expectEq(t, sl.PopFront(), 2) 84 | expectEq(t, sl.PopFront(), 1) 85 | expectPanic(t, func() { sl.PopFront() }) 86 | } 87 | 88 | func Test_SList_Reverse(t *testing.T) { 89 | sl := SListOf(1, 2, 3, 4) 90 | sl.Reverse() 91 | expectTrue(t, Equal(sl.Values(), []int{4, 3, 2, 1})) 92 | } 93 | 94 | func Test_SList_ForEach(t *testing.T) { 95 | sl := SListOf(1, 2, 3, 4) 96 | i := 0 97 | sl.ForEach(func(v int) { 98 | i++ 99 | expectEq(t, v, i) 100 | }) 101 | expectEq(t, i, sl.Len()) 102 | } 103 | 104 | func Test_SList_ForEachIf(t *testing.T) { 105 | sl := SListOf(1, 2, 3, 4) 106 | i := 0 107 | sl.ForEachIf(func(v int) bool { 108 | i++ 109 | expectEq(t, v, i) 110 | return i < 3 111 | }) 112 | expectEq(t, i, 3) 113 | } 114 | 115 | func Test_SList_ForEachMutable(t *testing.T) { 116 | sl := SListOf(1, 2, 3, 4) 117 | i := 0 118 | sl.ForEachMutable(func(v *int) { 119 | i++ 120 | expectEq(t, *v, i) 121 | *v = -*v 122 | }) 123 | expectEq(t, i, sl.Len()) 124 | sl.ForEachMutable(func(v *int) { 125 | expectLt(t, *v, 0) 126 | }) 127 | } 128 | 129 | func Test_SList_ForEachMutableIf(t *testing.T) { 130 | sl := SListOf(1, 2, 3, 4) 131 | i := 0 132 | sl.ForEachMutableIf(func(v *int) bool { 133 | i++ 134 | expectEq(t, *v, i) 135 | return i < 3 136 | }) 137 | expectEq(t, i, 3) 138 | } 139 | 140 | func Test_SList_Iterate(t *testing.T) { 141 | sl := SList[int]{} 142 | sl.PushBack(1) 143 | sl.PushBack(2) 144 | sl.PushBack(3) 145 | i := 0 146 | for it := sl.Iterate(); it.IsNotEnd(); it.MoveToNext() { 147 | i++ 148 | expectEq(t, it.Value(), i) 149 | expectEq(t, *it.Pointer(), i) 150 | } 151 | expectEq(t, i, 3) 152 | } 153 | -------------------------------------------------------------------------------- /sort.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "sort" 5 | ) 6 | 7 | // IsSorted returns whether the slice a is sorted in ascending order. 8 | // 9 | // Complexity: O(len(a)). 10 | func IsSorted[T Ordered](a []T) bool { 11 | if len(a) == 0 { 12 | return true 13 | } 14 | prev := a[0] 15 | for _, v := range a[1:] { 16 | if v < prev { 17 | return false 18 | } 19 | prev = v 20 | } 21 | return true 22 | } 23 | 24 | // IsDescSorted returns whether the slice a is sorted in descending order. 25 | // 26 | // Complexity: O(len(a)). 27 | func IsDescSorted[T Ordered](a []T) bool { 28 | if len(a) == 0 { 29 | return true 30 | } 31 | prev := a[0] 32 | for _, v := range a[1:] { 33 | if v > prev { 34 | return false 35 | } 36 | prev = v 37 | } 38 | return true 39 | } 40 | 41 | type ascSlice[T Ordered] []T 42 | 43 | func (x ascSlice[T]) Len() int { return len(x) } 44 | func (x ascSlice[T]) Less(i, j int) bool { return x[i] < x[j] } 45 | func (x ascSlice[T]) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 46 | 47 | // Sort sorts data in ascending order. 48 | // The order of equal elements is not guaranteed to be preserved. 49 | // 50 | // Complexity: O(N*log(N)), where N=len(a). 51 | func Sort[T Ordered](a []T) { 52 | sort.Sort(ascSlice[T](a)) 53 | } 54 | 55 | // StableSort sorts data in ascending order stably. 56 | // The order of equivalent elements is guaranteed to be preserved. 57 | // 58 | // Complexity: O(N*log(N)^2), where N=len(a). 59 | func StableSort[T Ordered](a []T) { 60 | sort.Stable(ascSlice[T](a)) 61 | } 62 | 63 | type descSlice[T Ordered] []T 64 | 65 | func (x descSlice[T]) Len() int { return len(x) } 66 | func (x descSlice[T]) Less(i, j int) bool { return x[i] > x[j] } 67 | func (x descSlice[T]) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 68 | 69 | // DescSort sorts data in descending order. 70 | // The order of equal elements is not guaranteed to be preserved. 71 | // 72 | // Complexity: O(N*log(N)), N=len(a). 73 | func DescSort[T Ordered](a []T) { 74 | sort.Sort(descSlice[T](a)) 75 | } 76 | 77 | // DescStableSort sorts data in descending order stably. 78 | // The order of equivalent elements is guaranteed to be preserved. 79 | // 80 | // Complexity: O(N*log(N)), N=len(a). 81 | func DescStableSort[T Ordered](a []T) { 82 | sort.Stable(descSlice[T](a)) 83 | } 84 | 85 | type funcSortable[T any] struct { 86 | e []T 87 | less LessFn[T] 88 | } 89 | 90 | func (x funcSortable[T]) Len() int { return len(x.e) } 91 | func (x funcSortable[T]) Less(i, j int) bool { return x.less(x.e[i], x.e[j]) } 92 | func (x funcSortable[T]) Swap(i, j int) { x.e[i], x.e[j] = x.e[j], x.e[i] } 93 | 94 | // SortFunc sorts data in ascending order with compare func less. 95 | // The order of equal elements is not guaranteed to be preserved. 96 | // 97 | // Complexity: O(N*log(N)), N=len(a). 98 | func SortFunc[T any](a []T, less func(x, y T) bool) { 99 | sort.Sort(funcSortable[T]{a, less}) 100 | } 101 | 102 | // StableSortFunc sorts data in ascending order with compare func less stably. 103 | // The order of equivalent elements is guaranteed to be preserved. 104 | // 105 | // Complexity: O(N*log(N)), N=len(a). 106 | func StableSortFunc[T any](a []T, less func(x, y T) bool) { 107 | sort.Stable(funcSortable[T]{a, less}) 108 | } 109 | -------------------------------------------------------------------------------- /sort_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_IsSorted(t *testing.T) { 8 | expectTrue(t, IsSorted([]int{})) 9 | expectTrue(t, IsSorted([]int{1, 2, 3, 4})) 10 | expectTrue(t, IsSorted([]int{1, 2, 2, 3, 4})) 11 | expectFalse(t, IsSorted([]int{1, 2, 2, 3, 2})) 12 | } 13 | 14 | func Test_IsDescSorted(t *testing.T) { 15 | expectTrue(t, IsDescSorted([]int{})) 16 | expectFalse(t, IsDescSorted([]int{1, 2, 3, 4})) 17 | expectFalse(t, IsDescSorted([]int{1, 2, 2, 3, 4})) 18 | expectTrue(t, IsDescSorted([]int{5, 4, 3, 3, 2})) 19 | } 20 | 21 | func Test_Sort(t *testing.T) { 22 | a := []int{5, 4, 3, 2, 1} 23 | Sort(a) 24 | expectTrue(t, IsSorted(a)) 25 | } 26 | 27 | func Test_DescSort(t *testing.T) { 28 | a := []int{1, 2, 3, 4} 29 | DescSort(a) 30 | expectTrue(t, IsDescSorted(a)) 31 | } 32 | 33 | func Test_StableSort(t *testing.T) { 34 | a := []int{5, 4, 3, 2, 1} 35 | StableSort(a) 36 | expectTrue(t, IsSorted(a)) 37 | } 38 | 39 | func Test_DescStableSort(t *testing.T) { 40 | a := []int{1, 2, 3, 4, 5} 41 | DescStableSort(a) 42 | expectTrue(t, IsDescSorted(a)) 43 | } 44 | 45 | func greater(x, y int) bool { return x > y } 46 | 47 | func Test_SortFunc(t *testing.T) { 48 | a := []int{1, 2, 3, 4, 5} 49 | SortFunc(a, greater) 50 | expectTrue(t, IsDescSorted(a)) 51 | } 52 | 53 | func Test_StableSortFunc(t *testing.T) { 54 | a := []int{1, 2, 3, 4, 5} 55 | StableSortFunc(a, greater) 56 | expectTrue(t, IsDescSorted(a)) 57 | } 58 | -------------------------------------------------------------------------------- /stack.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | // Stack s is a container adaptor that provides the functionality of a stack, 4 | // a LIFO (last-in, first-out) data structure. 5 | type Stack[T any] struct { 6 | elements []T 7 | } 8 | 9 | // NewStack creates a new Stack object. 10 | func NewStack[T any]() *Stack[T] { 11 | return &Stack[T]{nil} 12 | } 13 | 14 | // NewStackCap creates a new Stack object with the specified capicity. 15 | func NewStackCap[T any](capicity int) *Stack[T] { 16 | return &Stack[T]{make([]T, 0, capicity)} 17 | } 18 | 19 | // IsEmpty implements the Container interface. 20 | func (s Stack[T]) IsEmpty() bool { 21 | return len(s.elements) == 0 22 | } 23 | 24 | // Len implements the Container interface. 25 | func (s Stack[T]) Len() int { 26 | return len(s.elements) 27 | } 28 | 29 | // Cap returns the capacity of the stack. 30 | func (s Stack[T]) Cap() int { 31 | return cap(s.elements) 32 | } 33 | 34 | // Clear implements the Container interface. 35 | func (s *Stack[T]) Clear() { 36 | s.elements = s.elements[0:0] 37 | } 38 | 39 | // Push pushes the element to the top of the stack. 40 | func (s *Stack[T]) Push(t T) { 41 | s.elements = append(s.elements, t) 42 | } 43 | 44 | // TryPop tries to popup an element from the top of the stack. 45 | func (s *Stack[T]) TryPop() (val T, ok bool) { 46 | var t T 47 | if len(s.elements) == 0 { 48 | return t, false 49 | } 50 | t = s.elements[len(s.elements)-1] 51 | s.elements = s.elements[:len(s.elements)-1] 52 | return t, true 53 | } 54 | 55 | // Pop popups an element from the top of the stack. 56 | // It must be called when IsEmpty() returned false, 57 | // otherwise it will panic. 58 | func (s *Stack[T]) Pop() T { 59 | t := s.elements[len(s.elements)-1] 60 | s.elements = s.elements[:len(s.elements)-1] 61 | return t 62 | } 63 | 64 | // Top returns the top element in the stack. 65 | // It must be called when s.IsEmpty() returned false, 66 | // otherwise it will panic. 67 | func (s Stack[T]) Top() T { 68 | return s.elements[len(s.elements)-1] 69 | } 70 | -------------------------------------------------------------------------------- /stack_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_Stack_Interface(t *testing.T) { 8 | _ = Container(NewStack[int]()) 9 | } 10 | func Test_NewStack(t *testing.T) { 11 | si := NewStack[int]() 12 | ss := NewStack[string]() 13 | expectEq(t, si.Len(), 0) 14 | expectEq(t, ss.Len(), 0) 15 | } 16 | 17 | func Test_NewStackCap(t *testing.T) { 18 | s := NewStackCap[int](10) 19 | expectEq(t, s.Len(), 0) 20 | expectEq(t, s.Cap(), 10) 21 | } 22 | 23 | func Test_StackCap(t *testing.T) { 24 | s := NewStackCap[int](10) 25 | s.Push(1) 26 | expectEq(t, s.Len(), 1) 27 | expectEq(t, s.Cap(), 10) 28 | } 29 | 30 | func Test_Stack_Clear(t *testing.T) { 31 | s := NewStack[int]() 32 | s.Push(1) 33 | s.Push(2) 34 | s.Clear() 35 | expectEq(t, s.Len(), 0) 36 | expectTrue(t, s.IsEmpty()) 37 | } 38 | 39 | func Test_Stack_Push(t *testing.T) { 40 | s := NewStack[int]() 41 | s.Push(1) 42 | expectEq(t, s.Len(), 1) 43 | s.Push(2) 44 | expectEq(t, s.Len(), 2) 45 | } 46 | 47 | func Test_Stack_TryPop(t *testing.T) { 48 | s := NewStack[int]() 49 | _, ok := s.TryPop() 50 | expectFalse(t, ok) 51 | s.Push(1) 52 | v, ok := s.TryPop() 53 | expectTrue(t, ok) 54 | expectEq(t, v, 1) 55 | } 56 | 57 | func Test_Stack_Pop(t *testing.T) { 58 | s := NewStack[int]() 59 | s.Push(1) 60 | v := s.Pop() 61 | expectEq(t, v, 1) 62 | expectPanic(t, func() { s.Pop() }) 63 | } 64 | 65 | func Test_Stack_Top(t *testing.T) { 66 | s := NewStack[int]() 67 | s.Push(1) 68 | v := s.Top() 69 | expectEq(t, v, 1) 70 | s.Pop() 71 | expectPanic(t, func() { s.Top() }) 72 | } 73 | -------------------------------------------------------------------------------- /test_helper.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func report(t *testing.T, msg string) { 9 | t.Helper() 10 | t.Errorf("Wrong: %v\n", msg) 11 | } 12 | 13 | func reportMismatch[T comparable](t *testing.T, a T, op string, b T) { 14 | t.Helper() 15 | report(t, fmt.Sprintf("%v %v %v", a, op, b)) 16 | } 17 | 18 | func expectEq[T comparable](t *testing.T, a, b T) { 19 | t.Helper() 20 | if a != b { 21 | reportMismatch(t, a, "==", b) 22 | } 23 | } 24 | 25 | func expectNe[T comparable](t *testing.T, a, b T) { 26 | t.Helper() 27 | if !(a != b) { 28 | reportMismatch(t, a, "!=", b) 29 | } 30 | } 31 | 32 | func expectLt[T Ordered](t *testing.T, a, b T) { 33 | t.Helper() 34 | if !(a < b) { 35 | reportMismatch(t, a, "<", b) 36 | } 37 | } 38 | 39 | func expectGt[T Ordered](t *testing.T, a, b T) { 40 | t.Helper() 41 | if !(a > b) { 42 | reportMismatch(t, a, ">", b) 43 | } 44 | } 45 | 46 | func expectLe[T Ordered](t *testing.T, a, b T) { 47 | t.Helper() 48 | if !(a <= b) { 49 | reportMismatch(t, a, "<=", b) 50 | } 51 | } 52 | 53 | func expectGe[T Ordered](t *testing.T, a, b T) { 54 | t.Helper() 55 | if !(a >= b) { 56 | reportMismatch(t, a, ">=", b) 57 | } 58 | } 59 | 60 | func expectTrue(t *testing.T, actual bool) { 61 | t.Helper() 62 | if !actual { 63 | reportMismatch(t, actual, "==", true) 64 | } 65 | } 66 | 67 | func expectFalse(t *testing.T, actual bool) { 68 | t.Helper() 69 | if actual { 70 | reportMismatch(t, actual, "==", false) 71 | } 72 | } 73 | 74 | func expectPanic(t *testing.T, f func()) { 75 | t.Helper() 76 | defer func() { 77 | t.Helper() 78 | if r := recover(); r == nil { 79 | report(t, "din't panic") 80 | } 81 | }() 82 | 83 | f() 84 | } 85 | -------------------------------------------------------------------------------- /transform.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import "math/rand" 4 | 5 | // Copy make a copy of slice a. 6 | // 7 | // Complexity: O(len(a)). 8 | func Copy[T any](a []T) []T { 9 | return append([]T{}, a...) 10 | } 11 | 12 | // CopyTo copies all elements in slice a to slice to, return the copied slice. 13 | // if slice to is large enough, no memory allocation occurs. 14 | // 15 | // Complexity: O(len(a)). 16 | func CopyTo[T any](a []T, to []T) []T { 17 | b := append(to[0:0], a...) 18 | return b 19 | } 20 | 21 | // Fill fills each element in slice a with new value v. 22 | // 23 | // Complexity: O(len(a)). 24 | func Fill[T any](a []T, v T) { 25 | if len(a) == 0 { 26 | return 27 | } 28 | // Preload the first value into the array/slice. 29 | a[0] = v 30 | 31 | // Incrementally duplicate the value into the rest of the container. 32 | // About 2~3 times faster than naive fill. 33 | // https://gist.github.com/taylorza/df2f89d5f9ab3ffd06865062a4cf015d 34 | for j := 1; j < len(a); j *= 2 { 35 | copy(a[j:], a[:j]) 36 | } 37 | } 38 | 39 | // FillPattern fills elements in slice a with specified pattern. 40 | // 41 | // Complexity: O(len(a)). 42 | func FillPattern[T any](a []T, pattern []T) { 43 | if len(pattern) == 0 { 44 | panic("pattern can't be empty") 45 | } 46 | // Copy the pattern into the start of the container 47 | copy(a, pattern) 48 | 49 | // Incrementally duplicate the pattern throughout the container 50 | for j := len(pattern); j < len(a); j *= 2 { 51 | copy(a[j:], a[:j]) 52 | } 53 | } 54 | 55 | // TransformTo applies the function op to each element in slice a and fill it to slice b, 56 | // return the transformed slice. 57 | // If cap(b) >= len(a), no memory allocation. 58 | // 59 | // Complexity: O(len(a)). 60 | func TransformTo[R any, T any](a []T, op func(T) R, b []R) []R { 61 | b = b[0:0] 62 | for i := range a { 63 | b = append(b, op(a[i])) 64 | } 65 | return b 66 | } 67 | 68 | // Transform applies the function op to each element in slice a and set it back to the same place in a. 69 | // 70 | // Complexity: O(len(a)). 71 | func Transform[T any](a []T, op func(T) T) { 72 | for i, v := range a { 73 | a[i] = op(v) 74 | } 75 | } 76 | 77 | // TransformCopy applies the function op to each element in slice a and return all the result as a slice. 78 | // 79 | // Complexity: O(len(a)). 80 | func TransformCopy[R any, T any](a []T, op func(T) R) []R { 81 | r := make([]R, len(a)) 82 | for i, v := range a { 83 | r[i] = op(v) 84 | } 85 | return r 86 | } 87 | 88 | // Replace replaces every element that equals to old with new. 89 | // 90 | // Complexity: O(len(a)). 91 | func Replace[T comparable](a []T, old, new T) { 92 | for i := range a { 93 | if a[i] == old { 94 | a[i] = new 95 | } 96 | } 97 | } 98 | 99 | // ReplaceIf replaces every element that make preq returns true with new. 100 | // 101 | // Complexity: O(len(a)). 102 | func ReplaceIf[T any](a []T, pred func(v T) bool, new T) { 103 | for i := range a { 104 | if pred(a[i]) { 105 | a[i] = new 106 | } 107 | } 108 | } 109 | 110 | // Unique remove adjacent repeated elements from the input slice. 111 | // return the processed slice with new length. 112 | // 113 | // Complexity: O(len(a)). 114 | func Unique[T comparable](a []T) []T { 115 | if len(a) == 0 { 116 | return a 117 | } 118 | i := 1 119 | prev := a[0] 120 | for _, v := range a[1:] { 121 | if v != prev { 122 | a[i] = v 123 | i++ 124 | prev = v 125 | } 126 | } 127 | return a[:i] 128 | } 129 | 130 | // UniqueCopy remove adjacent repeated elements from the input slice. 131 | // return the result slice, and the input slice is kept unchanged. 132 | // 133 | // Complexity: O(len(a)). 134 | func UniqueCopy[T comparable](a []T) []T { 135 | var r []T 136 | if len(a) == 0 { 137 | return r 138 | } 139 | 140 | for _, v := range a { 141 | if len(r) > 0 && r[len(r)-1] == v { 142 | continue 143 | } 144 | r = append(r, v) 145 | } 146 | 147 | return r 148 | } 149 | 150 | // Remove remove the elements which equals to x from the input slice. 151 | // return the processed slice with new length. 152 | // 153 | // Complexity: O(len(a)). 154 | func Remove[T comparable](a []T, x T) []T { 155 | j := 0 156 | for _, v := range a { 157 | if v != x { 158 | a[j] = v 159 | j++ 160 | } 161 | } 162 | return a[:j] 163 | } 164 | 165 | // RemoveCopy remove all elements which equals to x from the input slice. 166 | // return a new slice with processed results. The input slice is kept unchanged. 167 | // 168 | // Complexity: O(len(a)). 169 | func RemoveCopy[T comparable](a []T, x T) []T { 170 | r := make([]T, 0, len(a)) 171 | for _, v := range a { 172 | if v != x { 173 | r = append(r, v) 174 | } 175 | } 176 | return r 177 | } 178 | 179 | // RemoveIf remove each element which make cond(x) returns true from the input slice, 180 | // copy other elements to a new slice and return it. 181 | // 182 | // Complexity: O(len(a)). 183 | func RemoveIf[T any](a []T, cond func(T) bool) []T { 184 | j := 0 185 | for _, v := range a { 186 | if !cond(v) { 187 | a[j] = v 188 | j++ 189 | } 190 | } 191 | return a[:j] 192 | } 193 | 194 | // RemoveIfCopy drops each element which make cond(x) returns true from the input slice, 195 | // copy other elements to a new slice and return it. The input slice is kept unchanged. 196 | // 197 | // Complexity: O(len(a)). 198 | func RemoveIfCopy[T any](a []T, cond func(T) bool) []T { 199 | r := make([]T, 0, len(a)) 200 | for _, v := range a { 201 | if !cond(v) { 202 | r = append(r, v) 203 | } 204 | } 205 | return r 206 | } 207 | 208 | // Shuffle pseudo-randomizes the order of elements. 209 | // 210 | // Complexity: O(len(a)). 211 | func Shuffle[T any](a []T) { 212 | rand.Shuffle(len(a), func(i, j int) { 213 | a[i], a[j] = a[j], a[i] 214 | }) 215 | } 216 | 217 | // Reverse reverses the order of the elements in the slice a. 218 | // 219 | // Complexity: O(len(a)). 220 | func Reverse[T any](a []T) { 221 | for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 { 222 | a[i], a[j] = a[j], a[i] 223 | } 224 | } 225 | 226 | // ReverseCopy returns a reversed copy of slice a. 227 | // 228 | // Complexity: O(len(a)). 229 | func ReverseCopy[T any](a []T) []T { 230 | b := make([]T, 0, len(a)) 231 | for i := len(a) - 1; i >= 0; i-- { 232 | b = append(b, a[i]) 233 | } 234 | return b 235 | } 236 | -------------------------------------------------------------------------------- /transform_fillzero_clear.go: -------------------------------------------------------------------------------- 1 | //go:build go1.21 2 | // +build go1.21 3 | 4 | package stl4go 5 | 6 | // FillZero fills each element in slice a with zero value. 7 | // 8 | // Complexity: O(len(a)). 9 | func FillZero[T any](a []T) { 10 | clear(a) 11 | } 12 | -------------------------------------------------------------------------------- /transform_fillzero_old.go: -------------------------------------------------------------------------------- 1 | //go:build !go1.21 2 | // +build !go1.21 3 | 4 | package stl4go 5 | 6 | // FillZero fills each element in slice a with zero value. 7 | // 8 | // Complexity: O(len(a)). 9 | func FillZero[T any](a []T) { 10 | var zero T 11 | Fill(a, zero) 12 | } 13 | -------------------------------------------------------------------------------- /transform_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | emptyInts = []int{} 10 | ) 11 | 12 | func Test_Copy(t *testing.T) { 13 | a := []int{1, 2, 3, 4} 14 | b := Copy(a) 15 | expectTrue(t, Equal(a, b)) 16 | } 17 | 18 | func Test_CopyTo(t *testing.T) { 19 | a := []int{1, 2, 3, 4} 20 | tos := [][]int{nil, make([]int, 0), make([]int, 10)} 21 | for _, to := range tos { 22 | b := CopyTo(a, to) 23 | expectTrue(t, Equal(a, b)) 24 | } 25 | } 26 | 27 | func Test_Fill(t *testing.T) { 28 | a := []int{1, 2, 3, 4} 29 | Fill(a, 1) 30 | expectTrue(t, Equal(a, []int{1, 1, 1, 1})) 31 | b := a[:0] 32 | Fill(b, 2) 33 | expectTrue(t, Equal(a, []int{1, 1, 1, 1})) 34 | } 35 | 36 | func Test_FillZero(t *testing.T) { 37 | a := []int{1, 2, 3, 4} 38 | FillZero(a) 39 | expectTrue(t, Equal(a, []int{0, 0, 0, 0})) 40 | } 41 | 42 | func naiveFill[T any](a []T, v T) { 43 | for i := range a { 44 | a[i] = v 45 | } 46 | } 47 | 48 | func BenchmarkFill(b *testing.B) { 49 | a := make([]int, 10000) 50 | b.Run("Fill", func(b *testing.B) { 51 | for i := 0; i < b.N; i++ { 52 | Fill(a, 1) 53 | } 54 | }) 55 | b.Run("naiveFill", func(b *testing.B) { 56 | for i := 0; i < b.N; i++ { 57 | naiveFill(a, 1) 58 | } 59 | }) 60 | } 61 | 62 | func Test_FillPattern(t *testing.T) { 63 | a := make([]int, 10) 64 | p := []int{1, 2, 3} 65 | FillPattern(a, p) 66 | expectTrue(t, Equal(a, []int{1, 2, 3, 1, 2, 3, 1, 2, 3, 1})) 67 | expectPanic(t, func() { FillPattern(a, []int{}) }) 68 | } 69 | 70 | func Test_Transform(t *testing.T) { 71 | a := Range(0, 100) 72 | Transform(a, func(v int) int { return v * v }) 73 | for i, v := range a { 74 | expectEq(t, v, i*i) 75 | } 76 | } 77 | 78 | func Test_TransformTo(t *testing.T) { 79 | a := Range(0, 100) 80 | b := make([]string, len(a)) 81 | r1 := TransformTo(a, func(v int) string { return strconv.Itoa(v) }, b) 82 | for i, v := range a { 83 | expectEq(t, b[i], strconv.Itoa(v)) 84 | } 85 | expectEq(t, &r1[0], &b[0]) 86 | c := make([]string, len(a)-1) 87 | r2 := TransformTo(a, func(v int) string { return strconv.Itoa(v) }, c) 88 | expectEq(t, len(a), len(r2)) 89 | expectNe(t, &r2[0], &c[0]) 90 | } 91 | 92 | func Test_TransformCopy(t *testing.T) { 93 | a := Range(0, 100) 94 | b := TransformCopy(a, func(v int) string { return strconv.Itoa(v) }) 95 | for i, v := range b { 96 | expectEq(t, v, strconv.Itoa(i)) 97 | } 98 | } 99 | 100 | func Test_Replace(t *testing.T) { 101 | a := []int{1, 2, 2, 4} 102 | Replace(a, 2, 3) 103 | expectTrue(t, Equal(a, []int{1, 3, 3, 4})) 104 | } 105 | 106 | func Test_ReplaceIf(t *testing.T) { 107 | a := []int{1, 2, 2, 4} 108 | ReplaceIf(a, func(n int) bool { return n == 2 }, 3) 109 | expectTrue(t, Equal(a, []int{1, 3, 3, 4})) 110 | } 111 | 112 | func Test_Unique(t *testing.T) { 113 | expectTrue(t, Equal(Unique(emptyInts), emptyInts)) 114 | a := []int{1, 2, 2, 3, 2, 4} 115 | b := []int{1, 2, 3, 2, 4} 116 | expectTrue(t, Equal(Unique(a), b)) 117 | expectTrue(t, Equal(Unique(a[:len(b)]), b)) 118 | } 119 | 120 | func Test_UniqueCopy(t *testing.T) { 121 | expectTrue(t, Equal(UniqueCopy(emptyInts), emptyInts)) 122 | a := []int{1, 2, 2, 3, 2, 4} 123 | a1 := append([]int{}, a...) 124 | b := []int{1, 2, 3, 2, 4} 125 | expectTrue(t, Equal(UniqueCopy(a), b)) 126 | expectTrue(t, Equal(a, a1)) 127 | } 128 | 129 | func Test_Remove(t *testing.T) { 130 | a := []int{1, 2, 2, 3, 2, 4} 131 | b := Remove(a, 2) 132 | expectTrue(t, Equal([]int{1, 3, 4}, b)) 133 | expectTrue(t, Equal([]int{1, 3, 4}, a[:len(b)])) 134 | } 135 | 136 | func Test_RemoveCopy(t *testing.T) { 137 | a := []int{1, 2, 2, 3, 2, 4} 138 | a1 := []int{1, 2, 2, 3, 2, 4} 139 | b := RemoveCopy(a, 2) 140 | expectTrue(t, Equal([]int{1, 3, 4}, b)) 141 | expectTrue(t, Equal(a1, a)) 142 | } 143 | 144 | func Test_RemoveIf(t *testing.T) { 145 | a := []int{1, 2, 2, 3, 2, 4} 146 | b := RemoveIf(a, func(v int) bool { return v == 2 }) 147 | expectTrue(t, Equal([]int{1, 3, 4}, b)) 148 | expectTrue(t, Equal([]int{1, 3, 4}, a[:len(b)])) 149 | } 150 | 151 | func Test_RemoveIfCopy(t *testing.T) { 152 | a := []int{1, 2, 2, 3, 2, 4} 153 | a1 := []int{1, 2, 2, 3, 2, 4} 154 | b := RemoveIfCopy(a, func(v int) bool { return v == 2 }) 155 | expectTrue(t, Equal([]int{1, 3, 4}, b)) 156 | expectTrue(t, Equal(a1, a)) 157 | } 158 | 159 | func Test_Shuffle(t *testing.T) { 160 | a := []int{1, 2, 3, 4, 5, 6} 161 | Shuffle(a) 162 | } 163 | 164 | func Test_Reverse(t *testing.T) { 165 | a := []int{1, 2, 3, 4} 166 | Reverse(a) 167 | expectTrue(t, Equal(a, []int{4, 3, 2, 1})) 168 | } 169 | 170 | func Test_Reverse_Odd(t *testing.T) { 171 | a := []int{1, 2, 3, 4, 5} 172 | Reverse(a) 173 | expectTrue(t, Equal(a, []int{5, 4, 3, 2, 1})) 174 | } 175 | 176 | func Test_ReverseCopy(t *testing.T) { 177 | a := []int{1, 2, 3, 4, 5} 178 | b := ReverseCopy(a) 179 | expectTrue(t, Equal(b, []int{5, 4, 3, 2, 1})) 180 | } 181 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | // Signed is a constraint that permits any signed integer type. 4 | // If future releases of Go add new predeclared signed integer types, 5 | // this constraint will be modified to include them. 6 | type Signed interface { 7 | ~int | ~int8 | ~int16 | ~int32 | ~int64 8 | } 9 | 10 | // Unsigned is a constraint that permits any unsigned integer type. 11 | // If future releases of Go add new predeclared unsigned integer types, 12 | // this constraint will be modified to include them. 13 | type Unsigned interface { 14 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr 15 | } 16 | 17 | // Integer is a constraint that permits any integer type. 18 | // If future releases of Go add new predeclared integer types, 19 | // this constraint will be modified to include them. 20 | type Integer interface { 21 | Signed | Unsigned 22 | } 23 | 24 | // Float is a constraint that permits any floating-point type. 25 | // If future releases of Go add new predeclared floating-point types, 26 | // this constraint will be modified to include them. 27 | type Float interface { 28 | ~float32 | ~float64 29 | } 30 | 31 | // Ordered is a constraint that permits any ordered type: any type 32 | // that supports the operators < <= >= >. 33 | // If future releases of Go add new ordered types, 34 | // this constraint will be modified to include them. 35 | type Ordered interface { 36 | Integer | Float | ~string 37 | } 38 | 39 | // Numeric is a constraint that permits any numeric type. 40 | type Numeric interface { 41 | Integer | Float 42 | } 43 | 44 | // LessFn is a function that returns whether 'a' is less than 'b'. 45 | type LessFn[T any] func(a, b T) bool 46 | 47 | // CompareFn is a 3 way compare function that 48 | // returns 1 if a > b, 49 | // returns 0 if a == b, 50 | // returns -1 if a < b. 51 | type CompareFn[T any] func(a, b T) int 52 | 53 | // HashFn is a function that returns the hash of 't'. 54 | type HashFn[T any] func(t T) uint64 55 | -------------------------------------------------------------------------------- /updatedoc.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | gomarkdoc -o generated_doc.md -e . 4 | -------------------------------------------------------------------------------- /updatedoc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | gomarkdoc -o generated_doc.md -e . 4 | -------------------------------------------------------------------------------- /vector.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | // Many tricks are from: 4 | // https://github.com/golang/go/wiki/SliceTricks#in-place-deduplicate-comparable 5 | 6 | // Vector is a sequence container representing array that can change in size. 7 | type Vector[T any] []T 8 | 9 | // MakeVector creates an empty Vector object. 10 | func MakeVector[T any]() Vector[T] { 11 | return (Vector[T])([]T{}) 12 | } 13 | 14 | // MakeVectorCap creates an empty Vector object with specified capacity. 15 | func MakeVectorCap[T any](c int) Vector[T] { 16 | v := make([]T, 0, c) 17 | return (Vector[T])(v) 18 | } 19 | 20 | // VectorOf creates a Vector object with initial values. 21 | func VectorOf[T any](v ...T) Vector[T] { 22 | return (Vector[T])(v) 23 | } 24 | 25 | // AsVector casts a slice as a Vector object. 26 | func AsVector[T any](s []T) Vector[T] { 27 | return (Vector[T])(s) 28 | } 29 | 30 | // IsEmpty implements the Container interface. 31 | func (v *Vector[T]) IsEmpty() bool { 32 | return len(*v) == 0 33 | } 34 | 35 | // Len implements the Container interface. 36 | func (v *Vector[T]) Len() int { 37 | return len(*v) 38 | } 39 | 40 | // Cap returns the capacity of the vector. 41 | func (v *Vector[T]) Cap() int { 42 | return cap(*v) 43 | } 44 | 45 | // Clear erases all elements from the vector. After this call, Len() returns zero. 46 | // Leaves the Cap() of the vector unchanged. 47 | func (v *Vector[T]) Clear() { 48 | FillZero(*v) 49 | *v = (*v)[0:0] 50 | } 51 | 52 | // Reserve increases the capacity of the vector (the total number of elements 53 | // that the vector can hold without requiring reallocation)to a value that's 54 | // greater or equal to l. If l is greater than the current Cap(), new storage 55 | // is allocated, otherwise the function does nothing. 56 | // 57 | // Reserve() does not change the size of the vector. 58 | func (v *Vector[T]) Reserve(l int) { 59 | if cap(*v) < l { 60 | t := make([]T, len(*v), l) 61 | copy(t, *v) 62 | *v = t 63 | } 64 | } 65 | 66 | // Shrink removes unused capacity from the vector. 67 | func (v *Vector[T]) Shrink() { 68 | if cap(*v) > len(*v) { 69 | *v = append([]T{}, *v...) 70 | } 71 | } 72 | 73 | // At returns the element value at the index i. 74 | // You can also use the [] operator, and it's better. 75 | func (v *Vector[T]) At(i int) T { 76 | return (*v)[i] 77 | } 78 | 79 | // Set sets the value of the element at the index i. 80 | // You can also use the [] operator, and it's better. 81 | func (v *Vector[T]) Set(i int, x T) { 82 | (*v)[i] = x 83 | } 84 | 85 | // PushBack pushs an element to the end of the vector. 86 | // 87 | // Complexity: O(1) if v.Len() < v.Cap(), therwise O(len(v)). 88 | func (v *Vector[T]) PushBack(x T) { 89 | *v = append(*v, x) 90 | } 91 | 92 | // PopBack popups an element from the end of the vector. 93 | // It must be called when IsEmpty() returned false, 94 | // otherwise it will panic. 95 | func (v *Vector[T]) PopBack() T { 96 | var zero T 97 | e := (*v)[v.Len()-1] 98 | (*v)[len(*v)-1] = zero 99 | *v = (*v)[0 : v.Len()-1] 100 | return e 101 | } 102 | 103 | // TryPopBack popups an element from the end of the vector. 104 | func (v *Vector[T]) TryPopBack() (T, bool) { 105 | if v.IsEmpty() { 106 | var zero T 107 | return zero, false 108 | } 109 | return v.PopBack(), true 110 | } 111 | 112 | // Back returns the element at the end of the vector. 113 | // It must be called when IsEmpty() returned false, 114 | // otherwise it will panic. 115 | func (v Vector[T]) Back() T { 116 | return v[len(v)-1] 117 | } 118 | 119 | // Append appends the values x... to the tail of the vector. 120 | func (v *Vector[T]) Append(x ...T) { 121 | *v = append(*v, x...) 122 | } 123 | 124 | // Insert inserts the values x... into the vector at index i. 125 | // After the insertion, (*v)[i] == x[0]. 126 | // Insert panics if i is out of range. 127 | // 128 | // Complexity: O(len(s) + len(v)). 129 | func (v *Vector[T]) Insert(i int, x ...T) { 130 | s := *v 131 | total := len(s) + len(x) 132 | if total <= cap(s) { 133 | s2 := s[:total] 134 | copy(s2[i+len(x):], s[i:]) 135 | copy(s2[i:], x) 136 | *v = s2 137 | return 138 | } 139 | s2 := make([]T, total) 140 | copy(s2, s[:i]) 141 | copy(s2[i:], x) 142 | copy(s2[i+len(x):], s[i:]) 143 | *v = s2 144 | } 145 | 146 | // Remove removes 1 element in the vector. 147 | // 148 | // Complexity: O(len(s) - i). 149 | func (v *Vector[T]) Remove(i int) { 150 | v.RemoveRange(i, i+1) 151 | } 152 | 153 | // RemoveRange removes the elements in the range[i, j) from the vector. 154 | func (v *Vector[T]) RemoveRange(i, j int) { 155 | oldV := *v 156 | *v = append((*v)[:i], (*v)[j:]...) 157 | FillZero(oldV[v.Len():]) 158 | } 159 | 160 | // RemoveLength removes the elements in the range[i, i+len) from the vector. 161 | func (v *Vector[T]) RemoveLength(i int, len int) { 162 | v.RemoveRange(i, i+len) 163 | } 164 | 165 | // RemoveIf removes the elements which make cond(x) returns true from the vector. 166 | func (v *Vector[T]) RemoveIf(cond func(T) bool) { 167 | oldV := *v 168 | *v = RemoveIf(*v, cond) 169 | FillZero(oldV[v.Len():]) 170 | } 171 | 172 | // ForEach iterate the container, apply each element to the cb callback function. 173 | func (v Vector[T]) ForEach(cb func(val T)) { 174 | for _, e := range v { 175 | cb(e) 176 | } 177 | } 178 | 179 | // ForEachIf iterate the container, apply each element to the cb callback function, 180 | // stop if cb returns false. 181 | func (v Vector[T]) ForEachIf(cb func(val T) bool) { 182 | for _, e := range v { 183 | if !cb(e) { 184 | break 185 | } 186 | } 187 | } 188 | 189 | // ForEachMutable iterate the container, apply pointer of each element to the cb callback function. 190 | func (v Vector[T]) ForEachMutable(cb func(val *T)) { 191 | for i := range v { 192 | cb(&v[i]) 193 | } 194 | } 195 | 196 | // ForEachMutableIf iterate the container, apply pointer of each element to the cb callback function, 197 | // stop if cb returns false. 198 | func (v Vector[T]) ForEachMutableIf(cb func(val *T) bool) { 199 | for i := range v { 200 | if !cb(&v[i]) { 201 | break 202 | } 203 | } 204 | } 205 | 206 | // Iterate returns an iterator to the whole container. 207 | func (v Vector[T]) Iterate() MutableIterator[T] { 208 | return &vectorIterator[T]{v, 0} 209 | } 210 | 211 | // IterateRange returns an iterator to the range [i, j) of the container. 212 | func (v Vector[T]) IterateRange(i, j int) MutableIterator[T] { 213 | return &vectorIterator[T]{v[i:j], 0} 214 | } 215 | 216 | type vectorIterator[T any] struct { 217 | v Vector[T] 218 | i int 219 | } 220 | 221 | func (it vectorIterator[T]) Value() T { 222 | return it.v[it.i] 223 | } 224 | 225 | func (it vectorIterator[T]) Pointer() *T { 226 | return &it.v[it.i] 227 | } 228 | 229 | func (it vectorIterator[T]) IsNotEnd() bool { 230 | return it.i < len(it.v) 231 | } 232 | 233 | func (it *vectorIterator[T]) MoveToNext() { 234 | it.i++ 235 | } 236 | -------------------------------------------------------------------------------- /vector_test.go: -------------------------------------------------------------------------------- 1 | package stl4go 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_Vector_Interface(t *testing.T) { 8 | v := MakeVector[int]() 9 | _ = Container(&v) 10 | } 11 | 12 | func Test_MakeVector(t *testing.T) { 13 | MakeVector[int]() 14 | _ = make(Vector[int], 1) 15 | _ = make(Vector[int], 1, 2) 16 | var v Vector[int] 17 | _ = v 18 | v = Vector[int]{1, 2, 3} 19 | } 20 | 21 | func Test_MakeVectorCap(t *testing.T) { 22 | v := MakeVectorCap[int](10) 23 | expectEq(t, v.Len(), 0) 24 | expectEq(t, v.Cap(), 10) 25 | } 26 | 27 | func Test_VectorOf(t *testing.T) { 28 | v := VectorOf(1, 2, 3) 29 | expectEq(t, v.Len(), 3) 30 | expectEq(t, v.Cap(), 3) 31 | expectEq(t, v[0], 1) 32 | expectEq(t, v[1], 2) 33 | expectEq(t, v[2], 3) 34 | } 35 | 36 | func Test_AsVector(t *testing.T) { 37 | s := []int{1, 2, 3} 38 | v := AsVector(s) 39 | expectTrue(t, Equal(s, v)) 40 | expectEq(t, &s[0], &v[0]) 41 | } 42 | 43 | func Test_VectorCap(t *testing.T) { 44 | v := MakeVectorCap[int](10) 45 | v.PushBack(1) 46 | expectEq(t, v.Len(), 1) 47 | expectFalse(t, v.IsEmpty()) 48 | expectEq(t, v.Cap(), 10) 49 | } 50 | 51 | func Test_Vector_Clear(t *testing.T) { 52 | v := VectorOf(1, 2, 3) 53 | v.Clear() 54 | expectEq(t, v.Len(), 0) 55 | expectTrue(t, v.IsEmpty()) 56 | expectGt(t, v.Cap(), 0) 57 | } 58 | 59 | func Test_Vector_Reserve(t *testing.T) { 60 | v := VectorOf(1, 2, 3) 61 | v.Reserve(1) 62 | expectEq(t, v.Cap(), 3) 63 | v.Reserve(5) 64 | expectEq(t, v.Cap(), 5) 65 | expectEq(t, v.Len(), 3) 66 | } 67 | 68 | func Test_Vector_Shrink(t *testing.T) { 69 | v := MakeVectorCap[int](10) 70 | v.Append(1, 2, 3) 71 | expectEq(t, v.Cap(), 10) 72 | v.Shrink() 73 | expectEq(t, v.Len(), v.Cap()) 74 | } 75 | 76 | func Test_Vector_At_Set(t *testing.T) { 77 | v := VectorOf(1, 2, 3) 78 | expectEq(t, v.At(0), 1) 79 | expectEq(t, v[0], 1) 80 | v[0] = 2 81 | expectEq(t, v[0], 2) 82 | expectPanic(t, func() { v.Set(3, 2) }) 83 | } 84 | 85 | func Test_Vector_PushBack(t *testing.T) { 86 | v := VectorOf(1, 2, 3) 87 | v.PushBack(4) 88 | expectTrue(t, Equal(v, []int{1, 2, 3, 4})) 89 | } 90 | 91 | func Test_Vector_PopBack(t *testing.T) { 92 | v := VectorOf(1, 2) 93 | expectEq(t, v.PopBack(), 2) 94 | n, ok := v.TryPopBack() 95 | expectEq(t, n, 1) 96 | expectTrue(t, ok) 97 | n, ok = v.TryPopBack() 98 | expectEq(t, n, 0) 99 | expectFalse(t, ok) 100 | expectPanic(t, func() { v.PopBack() }) 101 | } 102 | 103 | func Test_Vector_PopBack_Clear(t *testing.T) { 104 | v := VectorOf(1, 2) 105 | oldV := v 106 | v.PopBack() 107 | expectEq(t, oldV[1], 0) 108 | } 109 | 110 | func Test_Vector_Back(t *testing.T) { 111 | v := VectorOf(1) 112 | expectEq(t, v.Back(), 1) 113 | v.PopBack() 114 | expectPanic(t, func() { v.Back() }) 115 | } 116 | 117 | func Test_Vector_Insert(t *testing.T) { 118 | v := VectorOf(1, 2, 3) 119 | v.Insert(0, 1, 2, 3) 120 | expectTrue(t, Equal(v, []int{1, 2, 3, 1, 2, 3})) 121 | } 122 | 123 | func Test_Vector_Insert_Tail(t *testing.T) { 124 | v := VectorOf(1, 2, 3) 125 | v.Insert(3, 1, 2, 3) 126 | expectTrue(t, Equal(v, []int{1, 2, 3, 1, 2, 3})) 127 | } 128 | 129 | func Test_Vector_Insert_Mid(t *testing.T) { 130 | v := VectorOf(1, 2, 3) 131 | v.Insert(2, 1, 2) 132 | expectTrue(t, Equal(v, []int{1, 2, 1, 2, 3})) 133 | } 134 | 135 | func Test_Vector_Insert_Cap(t *testing.T) { 136 | v := VectorOf(1, 2, 3) 137 | v.Reserve(8) 138 | v.Insert(2, 1, 2) 139 | expectTrue(t, Equal(v, []int{1, 2, 1, 2, 3})) 140 | } 141 | 142 | func Test_Vector_Remove(t *testing.T) { 143 | v := VectorOf(1, 2, 3) 144 | oldV := v 145 | v.Remove(1) 146 | expectEq(t, v.Len(), 2) 147 | expectEq(t, v.Cap(), 3) 148 | expectEq(t, v[0], 1) 149 | expectEq(t, v[1], 3) 150 | expectEq(t, oldV[2], 0) 151 | } 152 | 153 | func Test_Vector_RemoveRange(t *testing.T) { 154 | v := VectorOf(1, 2, 3, 4) 155 | oldV := v 156 | v.RemoveRange(1, 3) 157 | expectEq(t, v.Len(), 2) 158 | expectEq(t, v.Cap(), 4) 159 | expectEq(t, v[0], 1) 160 | expectEq(t, v[1], 4) 161 | expectEq(t, oldV[2], 0) 162 | expectEq(t, oldV[3], 0) 163 | } 164 | 165 | func Test_Vector_RemoveLength(t *testing.T) { 166 | v := VectorOf(1, 2, 3, 4) 167 | oldV := v 168 | v.RemoveLength(1, 2) 169 | expectEq(t, v.Len(), 2) 170 | expectEq(t, v.Cap(), 4) 171 | expectEq(t, v[0], 1) 172 | expectEq(t, v[1], 4) 173 | expectEq(t, oldV[2], 0) 174 | expectEq(t, oldV[3], 0) 175 | } 176 | 177 | func Test_Vector_RemoveIf(t *testing.T) { 178 | v := VectorOf(1, 2, 3, 4) 179 | oldV := v 180 | v.RemoveIf(func(i int) bool { return i%2 == 0 }) 181 | expectEq(t, v.Len(), 2) 182 | expectEq(t, v.Cap(), 4) 183 | expectEq(t, v[0], 1) 184 | expectEq(t, v[1], 3) 185 | expectEq(t, oldV[2], 0) 186 | expectEq(t, oldV[3], 0) 187 | } 188 | 189 | func Test_Vector_ForEach(t *testing.T) { 190 | a := []int{1, 2, 3} 191 | v := VectorOf(a...) 192 | var b []int 193 | v.ForEach(func(n int) { 194 | b = append(b, n) 195 | }) 196 | expectEq(t, len(b), 3) 197 | expectTrue(t, Equal(a, b)) 198 | } 199 | 200 | func Test_Vector_ForEachIf(t *testing.T) { 201 | v := VectorOf(1, 2, 3) 202 | c := 0 203 | v.ForEachIf(func(n int) bool { 204 | c = n 205 | return n != 2 206 | }) 207 | expectEq(t, c, 2) 208 | } 209 | 210 | func Test_Vector_ForEachMutable(t *testing.T) { 211 | a := []int{1, 2, 3} 212 | v := VectorOf(1, 2, 3) 213 | v.ForEachMutable(func(n *int) { 214 | *n = -*n 215 | }) 216 | expectEq(t, v.Len(), 3) 217 | for i := range v { 218 | expectEq(t, a[i], -v[i]) 219 | } 220 | } 221 | 222 | func Test_Vector_ForEachMutableIf(t *testing.T) { 223 | v := VectorOf(1, 2, 3) 224 | c := 0 225 | v.ForEachMutableIf(func(n *int) bool { 226 | c = *n 227 | return *n != 2 228 | }) 229 | expectEq(t, c, 2) 230 | } 231 | 232 | func Test_Vector_Iterate(t *testing.T) { 233 | v := VectorOf(1, 2, 3, 4) 234 | i := 1 235 | for it := v.Iterate(); it.IsNotEnd(); it.MoveToNext() { 236 | expectEq(t, it.Value(), i) 237 | expectEq(t, *it.Pointer(), it.Value()) 238 | i++ 239 | } 240 | expectEq(t, i, 5) 241 | } 242 | 243 | func Test_Vector_IterateRange(t *testing.T) { 244 | v := VectorOf(1, 2, 3, 4) 245 | i := 2 246 | for it := v.IterateRange(1, 3); it.IsNotEnd(); it.MoveToNext() { 247 | expectEq(t, it.Value(), i) 248 | expectEq(t, *it.Pointer(), it.Value()) 249 | i++ 250 | } 251 | expectEq(t, i, 4) 252 | } 253 | --------------------------------------------------------------------------------