├── .circleci
└── config.yml
├── .gitignore
├── .mvn
└── wrapper
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── LICENSE
├── README.md
├── mvnw
├── mvnw.cmd
├── nbactions.xml
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── github
│ └── coderodde
│ └── util
│ ├── Finger.java
│ ├── FingerList.java
│ ├── IndexedLinkedList.java
│ ├── Node.java
│ └── benchmark
│ ├── LinkedListBenchmark.java
│ ├── LinkedListBenchmark2.java
│ └── LinkedListBenchmarkRunner.java
└── test
└── java
└── com
└── github
└── coderodde
└── util
├── FingerListTest.java
└── IndexedLinkedListTest.java
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | # Use the latest 2.1 version of CircleCI pipeline process engine.
2 | # See: https://circleci.com/docs/2.0/configuration-reference
3 | version: 2.1
4 | orbs:
5 | codecov: codecov/codecov@3.3.0
6 | workflows:
7 | upload-to-codecov:
8 | jobs:
9 | - checkout
10 | - codecov/upload
11 |
12 | #jobs:
13 | # build:
14 | # docker:
15 | # - image: circleci/clojure:openjdk-18-lein-2.9.8-buster-node-browsers-legacy
16 | # steps:
17 | # - checkout
18 | # - run : mvn test
19 | # - store_artifacts:
20 | # path: target
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /nbproject/
2 | /target/
3 | nb-configuration.xml
4 | performance_tables.html
5 | .idea
6 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderodde/IndexedLinkedList/0fc4e573ca5b0c720a34f824ff2292d79fe5e183/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.4/apache-maven-3.9.4-bin.zip
18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021, 2022 Rodion "coderodde" Efremov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://dl.circleci.com/status-badge/redirect/gh/coderodde/IndexedLinkedList/tree/main)
2 | [](https://codecov.io/gh/coderodde/IndexedLinkedList)
3 | [](https://opensource.org/licenses/MIT)
4 | [](https://search.maven.org/search?q=g:%22io.github.coderodde%22%20AND%20a:%22IndexedLinkedList%22)
5 | [](https://javadoc.io/doc/io.github.coderodde/IndexedLinkedList)
6 |
7 | # IndexedLinkedList - an indexed, heuristic doubly-linked list in Java
8 |
9 | This repository maintains the implementation of a linked list data structure that runs single-element operations in Θ(sqrt(n)) time under mild assumptions.
10 |
11 | The blog post explaining the (simple, high school level) math behind this data structure may be found behind [this link](http://coderodde.github.io/weblog/#meill).
12 |
13 | Our `IndexedLinkedList` exhibits performance faster than [Apache Commons Collections4 `org.apache.commons.collections4.list.TreeList.java`](https://github.com/apache/commons-collections/blob/master/src/main/java/org/apache/commons/collections4/list/TreeList.java) (which runs all the single-element operations in O(log(N)) time due to the AVL-tree algorithm), while (apart from having ceil(sqrt(N)) fingers, each consisting from a reference and an `int` value) having smaller memory footprint: for each node, our list maintains 3 references; each node in the `TreeList` consists of 3 references, 2 `int` values and 2 `boolean` values.
14 |
15 | ## Running time comparison
16 |
17 | | Operation | ArrayList | LinkedList | IndexedLinkedList | TreeList |
18 | | ---------------- | -------------- | -------------- | ------------------------- | ------------------------ |
19 | | `add(int)` | ***O(n)*** | ***O(n)*** | ***O(sqrt(n))*** | ***O(log n)*** |
20 | | `addFirst` | ***O(n)*** | ***O(1)*** | ***O(sqrt(n))*** | ***O(log n)*** |
21 | | `addLast` | ***O(1)*** | ***O(1)*** | ***O(1)*** | ***O(log n)*** |
22 | | `get` | ***O(1)*** | ***O(n)*** | ***O(sqrt(n))*** | ***O(log n)*** |
23 | | `remove(int)` | ***O(n)*** | ***O(n)*** | ***O(sqrt(n))*** | ***O(log n)*** |
24 | | `removeFirst` | ***O(n)*** | ***O(1)*** | ***O(sqrt(n))*** | ***O(log n)*** |
25 | | `removeLast` | ***O(1)*** | ***O(1)*** | ***O(1)*** | ***O(log n)*** |
26 | | `remove(Object)` | ***O(n)*** | ***O(n)*** | ***O(n)*** | ***O(n)*** |
27 | | `setAll` | ***O(n)*** | ***O(n)*** | ***O(n)*** | ***O(n)*** |
28 | | `prependAll` | ***O(m + n)*** | ***O(m)*** | ***O(m + sqrt(n))*** | ***O(m log n)*** |
29 | | `appendAll` | ***O(m)*** | ***O(m)*** | ***O(m)*** | ***O(m + log n)*** |
30 | | `insertAll` | ***O(m + n)*** | ***O(m + n)*** | ***O(m + sqrt(n))*** | ***O(m log n)*** |
31 | | `removeAll` | ***O(nf)*** | ***O(nf)*** | ***O(nf + n * sqrt(n))*** | ***O(nf + n * log(n))*** | |
32 |
33 | Above, ***n*** is the current size of a list, ***m*** is the size of a newly added collection, and ***f*** is the cost of consulting whether an element is contained in a filter collectoin.
34 |
35 | ## Benchmark output
36 | On a PC with a quad-core CPU with base speed 1,99 GHz and 256 kB L1 cache, 1 MB L2 cache and 8 MB L3 cache, the benchmark gives typically the following results:
37 |
38 | ### Benchmark with JMH
39 |
40 | #### Small size data
41 |
42 | | Operation | ms |
43 | |----------------------------------------------------|------------|
44 | | profileArrayListAddAtIndex | 0,074 |
45 | | profileCursorableLinkedListAddAtIndex | 0,185 |
46 | | profileJavaLinkedListAddAtIndex | 0,174 |
47 | | profileNodeCachingLinkedListAddAtIndex | 0,181 |
48 | | profileRoddeListAddAtIndex | 0,116 |
49 | | profileTreeListAddAtIndex | 0,153 |
50 | | | |
51 | | profileArrayListAddCollection | 0,127 |
52 | | profileCursorableLinkedListAddCollection | 0,151 |
53 | | profileJavaLinkedListAddCollection | 0,140 |
54 | | profileNodeCachingLinkedListAddCollection | 0,223 |
55 | | profileRoddeListAddCollection | 0,133 |
56 | | profileTreeListAddCollection | 0,413 |
57 | | | |
58 | | profileArrayListAddCollectionAtIndex | 0,375 |
59 | | profileCursorableLinkedListAddCollectionAtIndex | 2,989 |
60 | | profileJavaLinkedListAddCollectionAtIndex | 2,899 |
61 | | profileNodeCachingLinkedListAddCollectionAtIndex | 3,079 |
62 | | profileRoddeListAddCollectionAtIndex | 0,327 |
63 | | profileTreeListAddCollectionAtIndex | 1,944 |
64 | | | |
65 | | profileArrayListAddFirst | 0,074 |
66 | | profileCursorableLinkedListAddFirst | 0,008 |
67 | | profileJavaLinkedListAddFirst | 0,008 |
68 | | profileNodeCachingLinkedListAddFirst | 0,011 |
69 | | profileRoddeListAddFirst | 0,046 |
70 | | profileTreeListAddFirst | 0,086 |
71 | | | |
72 | | profileArrayListAddLast | 0,006 |
73 | | profileCursorableLinkedListAddLast | 0,007 |
74 | | profileJavaLinkedListAddLast | 0,006 |
75 | | profileNodeCachingLinkedListAddLast | 0,015 |
76 | | profileRoddeListAddLast | 0,010 |
77 | | profileTreeListAddLast | 0,082 |
78 | | | |
79 | | profileArrayListGet | 0,015 |
80 | | profileCursorableLinkedListGet | 3,738 |
81 | | profileJavaLinkedListGet | 3,721 |
82 | | profileNodeCachingLinkedListGet | 3,758 |
83 | | profileRoddeListGet | 0,144 |
84 | | profileTreeListGet | 0,117 |
85 | | | |
86 | | profileArrayListRemoveAll | 0,178 |
87 | | profileCursorableLinkedListRemoveAll | 0,388 |
88 | | profileJavaLinkedListRemoveAll | 19,543 |
89 | | profileNodeCachingLinkedListRemoveAll | 0,377 |
90 | | profileRoddeListRemoveAll | 0,360 |
91 | | profileTreeListRemoveAll | 56,304 |
92 | | | |
93 | | profileArrayListRemoveAtIndex | 1,986 |
94 | | profileCursorableLinkedListRemoveAtIndex | 24,609 |
95 | | profileJavaLinkedListRemoveAtIndex | 25,117 |
96 | | profileNodeCachingLinkedListRemoveAtIndex | 28,070 |
97 | | profileRoddeListRemoveAtIndex | 5,798 |
98 | | profileTreeListRemoveAtIndex | 2,978 |
99 | | | |
100 | | profileArrayListRemoveFirst | 2,753 |
101 | | profileCursorableLinkedListRemoveFirst | 0,955 |
102 | | profileJavaLinkedListRemoveFirst | 0,139 |
103 | | profileNodeCachingLinkedListRemoveFirst | 0,346 |
104 | | profileRoddeListRemoveFirst | 1,176 |
105 | | profileTreeListRemoveFirst | 1,060 |
106 | | | |
107 | | profileArrayListRemoveLast | 0,031 |
108 | | profileCursorableLinkedListRemoveLast | 0,266 |
109 | | profileJavaLinkedListRemoveLast | 0,136 |
110 | | profileNodeCachingLinkedListRemoveLast | 0,379 |
111 | | profileRoddeListRemoveLast | 0,204 |
112 | | profileTreeListRemoveLast | 1,348 |
113 | | | |
114 | | profileArrayListRemoveObject | 4,929 |
115 | | profileCursorableLinkedListRemoveObject | 29,639 |
116 | | profileJavaLinkedListRemoveObject | 10,105 |
117 | | profileNodeCachingLinkedListRemoveObject | 11,481 |
118 | | profileRoddeListRemoveObject | 21,035 |
119 | | profileTreeListRemoveObject | 30,990 |
120 | | | |
121 | | profileArrayListRemoveRange | 0,051 |
122 | | profileCursorableLinkedListRemoveRange | 11,476 |
123 | | profileJavaLinkedListRemoveRange | 0,540 |
124 | | profileNodeCachingLinkedListRemoveRange | 1,819 |
125 | | profileRoddeListRemoveRange | 0,574 |
126 | | profileTreeListRemoveRange | 7,156 |
127 | | | |
128 | | profileArrayListSortRange | 1,412 |
129 | | profileCursorableLinkedListSortRange | 8,641 |
130 | | profileJavaLinkedListSortRange | 1,547 |
131 | | profileNodeCachingLinkedListSortRange | 1,864 |
132 | | profileRoddeListSortRange | 1,488 |
133 | | profileTreeListSortRange | 1,792 |
134 | | | |
135 | | Total of ArrayList | 12,012 |
136 | | Total of JavaLinkedList | 64,074 |
137 | | Total of RoddeList | 31,410 |
138 | | Total of TreeList | 104,422 |
139 | | Total of NodeCachingLinkedList | 51,604 |
140 | | Total of CursorableLinkedList | 83,052 |
141 |
142 | #### Medium size data
143 |
144 | | Operation | ms |
145 | |----------------------------------------------------|------------|
146 | | profileArrayListAddAtIndex | 0,397 |
147 | | profileCursorableLinkedListAddAtIndex | 2,668 |
148 | | profileJavaLinkedListAddAtIndex | 2,653 |
149 | | profileNodeCachingLinkedListAddAtIndex | 2,705 |
150 | | profileRoddeListAddAtIndex | 0,432 |
151 | | profileTreeListAddAtIndex | 0,549 |
152 | | | |
153 | | profileArrayListAddCollection | 0,393 |
154 | | profileCursorableLinkedListAddCollection | 0,425 |
155 | | profileJavaLinkedListAddCollection | 0,439 |
156 | | profileNodeCachingLinkedListAddCollection | 0,554 |
157 | | profileRoddeListAddCollection | 0,405 |
158 | | profileTreeListAddCollection | 1,314 |
159 | | | |
160 | | profileArrayListAddCollectionAtIndex | 2,108 |
161 | | profileCursorableLinkedListAddCollectionAtIndex | 37,327 |
162 | | profileJavaLinkedListAddCollectionAtIndex | 35,409 |
163 | | profileNodeCachingLinkedListAddCollectionAtIndex | 36,833 |
164 | | profileRoddeListAddCollectionAtIndex | 1,328 |
165 | | profileTreeListAddCollectionAtIndex | 6,087 |
166 | | | |
167 | | profileArrayListAddFirst | 0,404 |
168 | | profileCursorableLinkedListAddFirst | 0,024 |
169 | | profileJavaLinkedListAddFirst | 0,020 |
170 | | profileNodeCachingLinkedListAddFirst | 0,049 |
171 | | profileRoddeListAddFirst | 0,140 |
172 | | profileTreeListAddFirst | 0,294 |
173 | | | |
174 | | profileArrayListAddLast | 0,016 |
175 | | profileCursorableLinkedListAddLast | 0,020 |
176 | | profileJavaLinkedListAddLast | 0,019 |
177 | | profileNodeCachingLinkedListAddLast | 0,045 |
178 | | profileRoddeListAddLast | 0,032 |
179 | | profileTreeListAddLast | 0,274 |
180 | | | |
181 | | profileArrayListGet | 0,056 |
182 | | profileCursorableLinkedListGet | 35,485 |
183 | | profileJavaLinkedListGet | 46,542 |
184 | | profileNodeCachingLinkedListGet | 36,150 |
185 | | profileRoddeListGet | 0,679 |
186 | | profileTreeListGet | 0,501 |
187 | | | |
188 | | profileArrayListRemoveAll | 0,631 |
189 | | profileCursorableLinkedListRemoveAll | 1,259 |
190 | | profileJavaLinkedListRemoveAll | 222,924 |
191 | | profileNodeCachingLinkedListRemoveAll | 1,138 |
192 | | profileRoddeListRemoveAll | 1,250 |
193 | | profileTreeListRemoveAll | 701,820 |
194 | | | |
195 | | profileArrayListRemoveAtIndex | 17,447 |
196 | | profileCursorableLinkedListRemoveAtIndex | 258,963 |
197 | | profileJavaLinkedListRemoveAtIndex | 264,451 |
198 | | profileNodeCachingLinkedListRemoveAtIndex | 326,732 |
199 | | profileRoddeListRemoveAtIndex | 49,221 |
200 | | profileTreeListRemoveAtIndex | 10,607 |
201 | | | |
202 | | profileArrayListRemoveFirst | 29,851 |
203 | | profileCursorableLinkedListRemoveFirst | 3,229 |
204 | | profileJavaLinkedListRemoveFirst | 0,643 |
205 | | profileNodeCachingLinkedListRemoveFirst | 0,975 |
206 | | profileRoddeListRemoveFirst | 5,066 |
207 | | profileTreeListRemoveFirst | 3,738 |
208 | | | |
209 | | profileArrayListRemoveLast | 0,089 |
210 | | profileCursorableLinkedListRemoveLast | 0,791 |
211 | | profileJavaLinkedListRemoveLast | 0,637 |
212 | | profileNodeCachingLinkedListRemoveLast | 1,141 |
213 | | profileRoddeListRemoveLast | 0,572 |
214 | | profileTreeListRemoveLast | 5,143 |
215 | | | |
216 | | profileArrayListRemoveObject | 46,302 |
217 | | profileCursorableLinkedListRemoveObject | 270,996 |
218 | | profileJavaLinkedListRemoveObject | 93,453 |
219 | | profileNodeCachingLinkedListRemoveObject | 106,020 |
220 | | profileRoddeListRemoveObject | 168,869 |
221 | | profileTreeListRemoveObject | 299,543 |
222 | | | |
223 | | profileArrayListRemoveRange | 0,097 |
224 | | profileCursorableLinkedListRemoveRange | 20,298 |
225 | | profileJavaLinkedListRemoveRange | 1,186 |
226 | | profileNodeCachingLinkedListRemoveRange | 3,278 |
227 | | profileRoddeListRemoveRange | 1,155 |
228 | | profileTreeListRemoveRange | 14,247 |
229 | | | |
230 | | profileArrayListSortRange | 8,417 |
231 | | profileCursorableLinkedListSortRange | 18,045 |
232 | | profileJavaLinkedListSortRange | 9,346 |
233 | | profileNodeCachingLinkedListSortRange | 10,747 |
234 | | profileRoddeListSortRange | 9,196 |
235 | | profileTreeListSortRange | 10,697 |
236 | | | |
237 | | Total of ArrayList | 106,209 |
238 | | Total of JavaLinkedList | 677,722 |
239 | | Total of RoddeList | 238,345 |
240 | | Total of TreeList | 1054,816 |
241 | | Total of NodeCachingLinkedList | 526,367 |
242 | | Total of CursorableLinkedList | 649,529 |
243 |
244 |
245 | #### Larger size data
246 |
247 |
248 | | Operation | ms |
249 | |----------------------------------------------------|------------|
250 | | profileArrayListAddAtIndex | 0,663 |
251 | | profileCursorableLinkedListAddAtIndex | 8,430 |
252 | | profileJavaLinkedListAddAtIndex | 8,413 |
253 | | profileNodeCachingLinkedListAddAtIndex | 8,527 |
254 | | profileRoddeListAddAtIndex | 0,867 |
255 | | profileTreeListAddAtIndex | 1,035 |
256 | | | |
257 | | profileArrayListAddCollection | 0,619 |
258 | | profileCursorableLinkedListAddCollection | 0,749 |
259 | | profileJavaLinkedListAddCollection | 0,671 |
260 | | profileNodeCachingLinkedListAddCollection | 1,115 |
261 | | profileRoddeListAddCollection | 0,675 |
262 | | profileTreeListAddCollection | 2,452 |
263 | | | |
264 | | profileArrayListAddCollectionAtIndex | 5,163 |
265 | | profileCursorableLinkedListAddCollectionAtIndex | 103,718 |
266 | | profileJavaLinkedListAddCollectionAtIndex | 100,904 |
267 | | profileNodeCachingLinkedListAddCollectionAtIndex | 108,378 |
268 | | profileRoddeListAddCollectionAtIndex | 2,540 |
269 | | profileTreeListAddCollectionAtIndex | 10,566 |
270 | | | |
271 | | profileArrayListAddFirst | 1,382 |
272 | | profileCursorableLinkedListAddFirst | 0,040 |
273 | | profileJavaLinkedListAddFirst | 0,044 |
274 | | profileNodeCachingLinkedListAddFirst | 0,058 |
275 | | profileRoddeListAddFirst | 0,268 |
276 | | profileTreeListAddFirst | 0,532 |
277 | | | |
278 | | profileArrayListAddLast | 0,024 |
279 | | profileCursorableLinkedListAddLast | 0,035 |
280 | | profileJavaLinkedListAddLast | 0,040 |
281 | | profileNodeCachingLinkedListAddLast | 0,067 |
282 | | profileRoddeListAddLast | 0,052 |
283 | | profileTreeListAddLast | 0,498 |
284 | | | |
285 | | profileArrayListGet | 0,096 |
286 | | profileCursorableLinkedListGet | 101,479 |
287 | | profileJavaLinkedListGet | 101,086 |
288 | | profileNodeCachingLinkedListGet | 102,238 |
289 | | profileRoddeListGet | 1,564 |
290 | | profileTreeListGet | 0,846 |
291 | | | |
292 | | profileArrayListRemoveAll | 1,041 |
293 | | profileCursorableLinkedListRemoveAll | 2,103 |
294 | | profileJavaLinkedListRemoveAll | 497,436 |
295 | | profileNodeCachingLinkedListRemoveAll | 2,278 |
296 | | profileRoddeListRemoveAll | 2,277 |
297 | | profileTreeListRemoveAll | 1699,747 |
298 | | | |
299 | | profileArrayListRemoveAtIndex | 49,006 |
300 | | profileCursorableLinkedListRemoveAtIndex | 755,302 |
301 | | profileJavaLinkedListRemoveAtIndex | 790,056 |
302 | | profileNodeCachingLinkedListRemoveAtIndex | 932,956 |
303 | | profileRoddeListRemoveAtIndex | 155,181 |
304 | | profileTreeListRemoveAtIndex | 17,946 |
305 | | | |
306 | | profileArrayListRemoveFirst | 80,048 |
307 | | profileCursorableLinkedListRemoveFirst | 5,888 |
308 | | profileJavaLinkedListRemoveFirst | 1,055 |
309 | | profileNodeCachingLinkedListRemoveFirst | 1,868 |
310 | | profileRoddeListRemoveFirst | 10,886 |
311 | | profileTreeListRemoveFirst | 6,530 |
312 | | | |
313 | | profileArrayListRemoveLast | 0,152 |
314 | | profileCursorableLinkedListRemoveLast | 1,367 |
315 | | profileJavaLinkedListRemoveLast | 1,104 |
316 | | profileNodeCachingLinkedListRemoveLast | 2,086 |
317 | | profileRoddeListRemoveLast | 1,216 |
318 | | profileTreeListRemoveLast | 8,458 |
319 | | | |
320 | | profileArrayListRemoveObject | 135,200 |
321 | | profileCursorableLinkedListRemoveObject | 768,759 |
322 | | profileJavaLinkedListRemoveObject | 258,027 |
323 | | profileNodeCachingLinkedListRemoveObject | 325,939 |
324 | | profileRoddeListRemoveObject | 529,313 |
325 | | profileTreeListRemoveObject | 856,094 |
326 | | | |
327 | | profileArrayListRemoveRange | 0,166 |
328 | | profileCursorableLinkedListRemoveRange | 18,914 |
329 | | profileJavaLinkedListRemoveRange | 1,922 |
330 | | profileNodeCachingLinkedListRemoveRange | 5,676 |
331 | | profileRoddeListRemoveRange | 1,889 |
332 | | profileTreeListRemoveRange | 22,228 |
333 | | | |
334 | | profileArrayListSortRange | 28,928 |
335 | | profileCursorableLinkedListSortRange | 52,450 |
336 | | profileJavaLinkedListSortRange | 32,352 |
337 | | profileNodeCachingLinkedListSortRange | 36,016 |
338 | | profileRoddeListSortRange | 31,444 |
339 | | profileTreeListSortRange | 36,604 |
340 | | | |
341 | | Total of ArrayList | 302,487 |
342 | | Total of JavaLinkedList | 1793,110 |
343 | | Total of RoddeList | 738,172 |
344 | | Total of TreeList | 2663,536 |
345 | | Total of NodeCachingLinkedList | 1527,201 |
346 | | Total of CursorableLinkedList | 1819,234 |
347 |
348 | ### Benchmark without JMH
349 |
350 | ```
351 | <<< Benchmark seed = 1654425745819 >>>
352 |
353 | <<< Flags >>>
354 | runSubListClear: true
355 | runRemoveAll : true
356 | runSort : true
357 |
358 | === WARMUP RUN ===
359 | com.github.coderodde.util.IndexedLinkedList.addFirst in (ms): 53
360 | java.util.LinkedList.addFirst in (ms): 11
361 | java.util.ArrayList.addFirst in (ms): 617
362 | org.apache.commons.collections4.list.TreeList.addFirst in (ms): 36
363 |
364 | com.github.coderodde.util.IndexedLinkedList.addLast in (ms): 0
365 | java.util.LinkedList.addLast in (ms): 32
366 | java.util.ArrayList.addLast in (ms): 18
367 | org.apache.commons.collections4.list.TreeList.addLast in (ms): 41
368 |
369 | com.github.coderodde.util.IndexedLinkedList.add(int, E) in (ms): 29
370 | java.util.LinkedList.add(int, E) in (ms): 1873
371 | java.util.ArrayList.add(int, E) in (ms): 115
372 | org.apache.commons.collections4.list.TreeList.add(int, E) in (ms): 28
373 |
374 | com.github.coderodde.util.IndexedLinkedList.addAll(Collection) in (ms): 9
375 | java.util.LinkedList.addAll(Collection) in (ms): 28
376 | java.util.ArrayList.addAll(Collection) in (ms): 4
377 | org.apache.commons.collections4.list.TreeList.addAll(Collection) in (ms): 31
378 |
379 | com.github.coderodde.util.IndexedLinkedList.addAll(int, Collection) in (ms): 38
380 | java.util.LinkedList.addAll(int, Collection) in (ms): 1546
381 | java.util.ArrayList.addAll(int, Collection) in (ms): 92
382 | org.apache.commons.collections4.list.TreeList.addAll(int, Collection) in (ms): 16
383 |
384 | com.github.coderodde.util.IndexedLinkedList.get(int) in (ms): 10
385 | java.util.LinkedList.get(int) in (ms): 2133
386 | java.util.ArrayList.get(int) in (ms): 1
387 | org.apache.commons.collections4.list.TreeList.get(int) in (ms): 3
388 |
389 | com.github.coderodde.util.IndexedLinkedList.removeFirst() in (ms): 29
390 | java.util.LinkedList.removeFirst() in (ms): 1
391 | java.util.ArrayList.removeFirst() in (ms): 243
392 | org.apache.commons.collections4.list.TreeList.removeFirst() in (ms): 3
393 |
394 | com.github.coderodde.util.IndexedLinkedList.removeLast() in (ms): 0
395 | java.util.LinkedList.removeLast() in (ms): 0
396 | java.util.ArrayList.removeLast() in (ms): 0
397 | org.apache.commons.collections4.list.TreeList.removeLast() in (ms): 0
398 |
399 | com.github.coderodde.util.IndexedLinkedList.remove(int) in (ms): 42
400 | java.util.LinkedList.remove(int) in (ms): 4067
401 | java.util.ArrayList.remove(int) in (ms): 216
402 | org.apache.commons.collections4.list.TreeList.remove(int) in (ms): 11
403 |
404 | com.github.coderodde.util.IndexedLinkedList.remove(Object) in (ms): 109
405 | java.util.LinkedList.remove(Object) in (ms): 56
406 | java.util.ArrayList.remove(Object) in (ms): 281
407 | org.apache.commons.collections4.list.TreeList.remove(Object) in (ms): 139
408 |
409 | com.github.coderodde.util.IndexedLinkedList.iterator().add() in (ms): 25
410 | java.util.LinkedList.iterator().add() in (ms): 3
411 | java.util.ArrayList.iterator().add() in (ms): 826
412 | org.apache.commons.collections4.list.TreeList.iterator().add() in (ms): 45
413 |
414 | com.github.coderodde.util.IndexedLinkedList.iterator().remove() in (ms): 6
415 | java.util.LinkedList.iterator().remove() in (ms): 15
416 | java.util.ArrayList.iterator().remove() in (ms): 925
417 | org.apache.commons.collections4.list.TreeList.iterator().remove() in (ms): 40
418 |
419 | com.github.coderodde.util.IndexedLinkedList.stream() in (ms): 26
420 | java.util.LinkedList.stream() in (ms): 10
421 | java.util.ArrayList.stream() in (ms): 10
422 | org.apache.commons.collections4.list.TreeList.stream() in (ms): 98
423 |
424 | com.github.coderodde.util.IndexedLinkedList.stream().parallel() in (ms): 59
425 | java.util.LinkedList.stream().parallel() in (ms): 55
426 | java.util.ArrayList.stream().parallel() in (ms): 17
427 | org.apache.commons.collections4.list.TreeList.stream().parallel() in (ms): 205
428 |
429 | com.github.coderodde.util.IndexedLinkedList.removeAll() in (ms): 2
430 | java.util.LinkedList.removeAll() in (ms): 126
431 | java.util.ArrayList.removeAll() in (ms): 2
432 | org.apache.commons.collections4.list.TreeList.removeAll() in (ms): 304
433 |
434 | com.github.coderodde.util.IndexedLinkedList.subList(...).clear() in (ms): 10
435 | java.util.LinkedList.subList(...).clear() in (ms): 39
436 | java.util.ArrayList.subList(...).clear() in (ms): 5
437 | org.apache.commons.collections4.list.TreeList.subList(...).clear() in (ms): 224
438 |
439 | IndexedLinkedList.subList().sort() in (ms): 409
440 | LinkedList.subList().sort() in (ms): 325
441 | ArrayList.subList().sort() in (ms): 271
442 | TreeList.subList().sort() in (ms): 329
443 |
444 | --- Total time elapsed ---
445 | com.github.coderodde.util.IndexedLinkedList in (ms): 856
446 | java.util.LinkedList in (ms): 10320
447 | java.util.ArrayList in (ms): 3643
448 | org.apache.commons.collections4.list.TreeList in (ms): 1553
449 |
450 | === BENCHMARK RUN ===
451 | com.github.coderodde.util.IndexedLinkedList.addFirst in (ms): 23
452 | java.util.LinkedList.addFirst in (ms): 3
453 | java.util.ArrayList.addFirst in (ms): 541
454 | org.apache.commons.collections4.list.TreeList.addFirst in (ms): 16
455 |
456 | com.github.coderodde.util.IndexedLinkedList.addLast in (ms): 8
457 | java.util.LinkedList.addLast in (ms): 2
458 | java.util.ArrayList.addLast in (ms): 157
459 | org.apache.commons.collections4.list.TreeList.addLast in (ms): 38
460 |
461 | com.github.coderodde.util.IndexedLinkedList.add(int, E) in (ms): 22
462 | java.util.LinkedList.add(int, E) in (ms): 1977
463 | java.util.ArrayList.add(int, E) in (ms): 112
464 | org.apache.commons.collections4.list.TreeList.add(int, E) in (ms): 7
465 |
466 | com.github.coderodde.util.IndexedLinkedList.addAll(Collection) in (ms): 12
467 | java.util.LinkedList.addAll(Collection) in (ms): 4
468 | java.util.ArrayList.addAll(Collection) in (ms): 4
469 | org.apache.commons.collections4.list.TreeList.addAll(Collection) in (ms): 7
470 |
471 | com.github.coderodde.util.IndexedLinkedList.addAll(int, Collection) in (ms): 11
472 | java.util.LinkedList.addAll(int, Collection) in (ms): 2084
473 | java.util.ArrayList.addAll(int, Collection) in (ms): 91
474 | org.apache.commons.collections4.list.TreeList.addAll(int, Collection) in (ms): 14
475 |
476 | com.github.coderodde.util.IndexedLinkedList.get(int) in (ms): 10
477 | java.util.LinkedList.get(int) in (ms): 2408
478 | java.util.ArrayList.get(int) in (ms): 0
479 | org.apache.commons.collections4.list.TreeList.get(int) in (ms): 1
480 |
481 | com.github.coderodde.util.IndexedLinkedList.removeFirst() in (ms): 8
482 | java.util.LinkedList.removeFirst() in (ms): 0
483 | java.util.ArrayList.removeFirst() in (ms): 210
484 | org.apache.commons.collections4.list.TreeList.removeFirst() in (ms): 11
485 |
486 | com.github.coderodde.util.IndexedLinkedList.removeLast() in (ms): 1
487 | java.util.LinkedList.removeLast() in (ms): 1
488 | java.util.ArrayList.removeLast() in (ms): 0
489 | org.apache.commons.collections4.list.TreeList.removeLast() in (ms): 0
490 |
491 | com.github.coderodde.util.IndexedLinkedList.remove(int) in (ms): 23
492 | java.util.LinkedList.remove(int) in (ms): 4684
493 | java.util.ArrayList.remove(int) in (ms): 202
494 | org.apache.commons.collections4.list.TreeList.remove(int) in (ms): 10
495 |
496 | com.github.coderodde.util.IndexedLinkedList.remove(Object) in (ms): 53
497 | java.util.LinkedList.remove(Object) in (ms): 38
498 | java.util.ArrayList.remove(Object) in (ms): 245
499 | org.apache.commons.collections4.list.TreeList.remove(Object) in (ms): 87
500 |
501 | com.github.coderodde.util.IndexedLinkedList.iterator().add() in (ms): 19
502 | java.util.LinkedList.iterator().add() in (ms): 10
503 | java.util.ArrayList.iterator().add() in (ms): 828
504 | org.apache.commons.collections4.list.TreeList.iterator().add() in (ms): 18
505 |
506 | com.github.coderodde.util.IndexedLinkedList.iterator().remove() in (ms): 10
507 | java.util.LinkedList.iterator().remove() in (ms): 16
508 | java.util.ArrayList.iterator().remove() in (ms): 1265
509 | org.apache.commons.collections4.list.TreeList.iterator().remove() in (ms): 43
510 |
511 | com.github.coderodde.util.IndexedLinkedList.stream() in (ms): 1
512 | java.util.LinkedList.stream() in (ms): 20
513 | java.util.ArrayList.stream() in (ms): 16
514 | org.apache.commons.collections4.list.TreeList.stream() in (ms): 15
515 |
516 | com.github.coderodde.util.IndexedLinkedList.stream().parallel() in (ms): 3
517 | java.util.LinkedList.stream().parallel() in (ms): 110
518 | java.util.ArrayList.stream().parallel() in (ms): 3
519 | org.apache.commons.collections4.list.TreeList.stream().parallel() in (ms): 206
520 |
521 | com.github.coderodde.util.IndexedLinkedList.removeAll() in (ms): 0
522 | java.util.LinkedList.removeAll() in (ms): 103
523 | java.util.ArrayList.removeAll() in (ms): 1
524 | org.apache.commons.collections4.list.TreeList.removeAll() in (ms): 256
525 |
526 | com.github.coderodde.util.IndexedLinkedList.subList(...).clear() in (ms): 4
527 | java.util.LinkedList.subList(...).clear() in (ms): 35
528 | java.util.ArrayList.subList(...).clear() in (ms): 2
529 | org.apache.commons.collections4.list.TreeList.subList(...).clear() in (ms): 235
530 |
531 | IndexedLinkedList.subList().sort() in (ms): 315
532 | LinkedList.subList().sort() in (ms): 321
533 | ArrayList.subList().sort() in (ms): 299
534 | TreeList.subList().sort() in (ms): 302
535 |
536 | --- Total time elapsed ---
537 | com.github.coderodde.util.IndexedLinkedList in (ms): 523
538 | java.util.LinkedList in (ms): 11816
539 | java.util.ArrayList in (ms): 3976
540 | org.apache.commons.collections4.list.TreeList in (ms): 1266
541 | ```
542 |
--------------------------------------------------------------------------------
/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Apache Maven Wrapper startup batch script, version 3.2.0
23 | #
24 | # Required ENV vars:
25 | # ------------------
26 | # JAVA_HOME - location of a JDK home dir
27 | #
28 | # Optional ENV vars
29 | # -----------------
30 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven
31 | # e.g. to debug Maven itself, use
32 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
33 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
34 | # ----------------------------------------------------------------------------
35 |
36 | if [ -z "$MAVEN_SKIP_RC" ] ; then
37 |
38 | if [ -f /usr/local/etc/mavenrc ] ; then
39 | . /usr/local/etc/mavenrc
40 | fi
41 |
42 | if [ -f /etc/mavenrc ] ; then
43 | . /etc/mavenrc
44 | fi
45 |
46 | if [ -f "$HOME/.mavenrc" ] ; then
47 | . "$HOME/.mavenrc"
48 | fi
49 |
50 | fi
51 |
52 | # OS specific support. $var _must_ be set to either true or false.
53 | cygwin=false;
54 | darwin=false;
55 | mingw=false
56 | case "$(uname)" in
57 | CYGWIN*) cygwin=true ;;
58 | MINGW*) mingw=true;;
59 | Darwin*) darwin=true
60 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
61 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
62 | if [ -z "$JAVA_HOME" ]; then
63 | if [ -x "/usr/libexec/java_home" ]; then
64 | JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
65 | else
66 | JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
67 | fi
68 | fi
69 | ;;
70 | esac
71 |
72 | if [ -z "$JAVA_HOME" ] ; then
73 | if [ -r /etc/gentoo-release ] ; then
74 | JAVA_HOME=$(java-config --jre-home)
75 | fi
76 | fi
77 |
78 | # For Cygwin, ensure paths are in UNIX format before anything is touched
79 | if $cygwin ; then
80 | [ -n "$JAVA_HOME" ] &&
81 | JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
82 | [ -n "$CLASSPATH" ] &&
83 | CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
84 | fi
85 |
86 | # For Mingw, ensure paths are in UNIX format before anything is touched
87 | if $mingw ; then
88 | [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
89 | JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
90 | fi
91 |
92 | if [ -z "$JAVA_HOME" ]; then
93 | javaExecutable="$(which javac)"
94 | if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
95 | # readlink(1) is not available as standard on Solaris 10.
96 | readLink=$(which readlink)
97 | if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
98 | if $darwin ; then
99 | javaHome="$(dirname "\"$javaExecutable\"")"
100 | javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
101 | else
102 | javaExecutable="$(readlink -f "\"$javaExecutable\"")"
103 | fi
104 | javaHome="$(dirname "\"$javaExecutable\"")"
105 | javaHome=$(expr "$javaHome" : '\(.*\)/bin')
106 | JAVA_HOME="$javaHome"
107 | export JAVA_HOME
108 | fi
109 | fi
110 | fi
111 |
112 | if [ -z "$JAVACMD" ] ; then
113 | if [ -n "$JAVA_HOME" ] ; then
114 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
115 | # IBM's JDK on AIX uses strange locations for the executables
116 | JAVACMD="$JAVA_HOME/jre/sh/java"
117 | else
118 | JAVACMD="$JAVA_HOME/bin/java"
119 | fi
120 | else
121 | JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
122 | fi
123 | fi
124 |
125 | if [ ! -x "$JAVACMD" ] ; then
126 | echo "Error: JAVA_HOME is not defined correctly." >&2
127 | echo " We cannot execute $JAVACMD" >&2
128 | exit 1
129 | fi
130 |
131 | if [ -z "$JAVA_HOME" ] ; then
132 | echo "Warning: JAVA_HOME environment variable is not set."
133 | fi
134 |
135 | # traverses directory structure from process work directory to filesystem root
136 | # first directory with .mvn subdirectory is considered project base directory
137 | find_maven_basedir() {
138 | if [ -z "$1" ]
139 | then
140 | echo "Path not specified to find_maven_basedir"
141 | return 1
142 | fi
143 |
144 | basedir="$1"
145 | wdir="$1"
146 | while [ "$wdir" != '/' ] ; do
147 | if [ -d "$wdir"/.mvn ] ; then
148 | basedir=$wdir
149 | break
150 | fi
151 | # workaround for JBEAP-8937 (on Solaris 10/Sparc)
152 | if [ -d "${wdir}" ]; then
153 | wdir=$(cd "$wdir/.." || exit 1; pwd)
154 | fi
155 | # end of workaround
156 | done
157 | printf '%s' "$(cd "$basedir" || exit 1; pwd)"
158 | }
159 |
160 | # concatenates all lines of a file
161 | concat_lines() {
162 | if [ -f "$1" ]; then
163 | # Remove \r in case we run on Windows within Git Bash
164 | # and check out the repository with auto CRLF management
165 | # enabled. Otherwise, we may read lines that are delimited with
166 | # \r\n and produce $'-Xarg\r' rather than -Xarg due to word
167 | # splitting rules.
168 | tr -s '\r\n' ' ' < "$1"
169 | fi
170 | }
171 |
172 | log() {
173 | if [ "$MVNW_VERBOSE" = true ]; then
174 | printf '%s\n' "$1"
175 | fi
176 | }
177 |
178 | BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
179 | if [ -z "$BASE_DIR" ]; then
180 | exit 1;
181 | fi
182 |
183 | MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
184 | log "$MAVEN_PROJECTBASEDIR"
185 |
186 | ##########################################################################################
187 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
188 | # This allows using the maven wrapper in projects that prohibit checking in binary data.
189 | ##########################################################################################
190 | wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
191 | if [ -r "$wrapperJarPath" ]; then
192 | log "Found $wrapperJarPath"
193 | else
194 | log "Couldn't find $wrapperJarPath, downloading it ..."
195 |
196 | if [ -n "$MVNW_REPOURL" ]; then
197 | wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
198 | else
199 | wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
200 | fi
201 | while IFS="=" read -r key value; do
202 | # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
203 | safeValue=$(echo "$value" | tr -d '\r')
204 | case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
205 | esac
206 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
207 | log "Downloading from: $wrapperUrl"
208 |
209 | if $cygwin; then
210 | wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
211 | fi
212 |
213 | if command -v wget > /dev/null; then
214 | log "Found wget ... using wget"
215 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
216 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
217 | wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
218 | else
219 | wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
220 | fi
221 | elif command -v curl > /dev/null; then
222 | log "Found curl ... using curl"
223 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
224 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
225 | curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
226 | else
227 | curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
228 | fi
229 | else
230 | log "Falling back to using Java to download"
231 | javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
232 | javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
233 | # For Cygwin, switch paths to Windows format before running javac
234 | if $cygwin; then
235 | javaSource=$(cygpath --path --windows "$javaSource")
236 | javaClass=$(cygpath --path --windows "$javaClass")
237 | fi
238 | if [ -e "$javaSource" ]; then
239 | if [ ! -e "$javaClass" ]; then
240 | log " - Compiling MavenWrapperDownloader.java ..."
241 | ("$JAVA_HOME/bin/javac" "$javaSource")
242 | fi
243 | if [ -e "$javaClass" ]; then
244 | log " - Running MavenWrapperDownloader.java ..."
245 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
246 | fi
247 | fi
248 | fi
249 | fi
250 | ##########################################################################################
251 | # End of extension
252 | ##########################################################################################
253 |
254 | # If specified, validate the SHA-256 sum of the Maven wrapper jar file
255 | wrapperSha256Sum=""
256 | while IFS="=" read -r key value; do
257 | case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
258 | esac
259 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
260 | if [ -n "$wrapperSha256Sum" ]; then
261 | wrapperSha256Result=false
262 | if command -v sha256sum > /dev/null; then
263 | if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
264 | wrapperSha256Result=true
265 | fi
266 | elif command -v shasum > /dev/null; then
267 | if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
268 | wrapperSha256Result=true
269 | fi
270 | else
271 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
272 | echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
273 | exit 1
274 | fi
275 | if [ $wrapperSha256Result = false ]; then
276 | echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
277 | echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
278 | echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
279 | exit 1
280 | fi
281 | fi
282 |
283 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
284 |
285 | # For Cygwin, switch paths to Windows format before running java
286 | if $cygwin; then
287 | [ -n "$JAVA_HOME" ] &&
288 | JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
289 | [ -n "$CLASSPATH" ] &&
290 | CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
291 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
292 | MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
293 | fi
294 |
295 | # Provide a "standardized" way to retrieve the CLI args that will
296 | # work with both Windows and non-Windows executions.
297 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
298 | export MAVEN_CMD_LINE_ARGS
299 |
300 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
301 |
302 | # shellcheck disable=SC2086 # safe args
303 | exec "$JAVACMD" \
304 | $MAVEN_OPTS \
305 | $MAVEN_DEBUG_OPTS \
306 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
307 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
308 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
309 |
--------------------------------------------------------------------------------
/mvnw.cmd:
--------------------------------------------------------------------------------
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM http://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Apache Maven Wrapper startup batch script, version 3.2.0
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
30 | @REM e.g. to debug Maven itself, use
31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
33 | @REM ----------------------------------------------------------------------------
34 |
35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
36 | @echo off
37 | @REM set title of command window
38 | title %0
39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
41 |
42 | @REM set %HOME% to equivalent of $HOME
43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
44 |
45 | @REM Execute a user defined script before this one
46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
50 | :skipRcPre
51 |
52 | @setlocal
53 |
54 | set ERROR_CODE=0
55 |
56 | @REM To isolate internal variables from possible post scripts, we use another setlocal
57 | @setlocal
58 |
59 | @REM ==== START VALIDATION ====
60 | if not "%JAVA_HOME%" == "" goto OkJHome
61 |
62 | echo.
63 | echo Error: JAVA_HOME not found in your environment. >&2
64 | echo Please set the JAVA_HOME variable in your environment to match the >&2
65 | echo location of your Java installation. >&2
66 | echo.
67 | goto error
68 |
69 | :OkJHome
70 | if exist "%JAVA_HOME%\bin\java.exe" goto init
71 |
72 | echo.
73 | echo Error: JAVA_HOME is set to an invalid directory. >&2
74 | echo JAVA_HOME = "%JAVA_HOME%" >&2
75 | echo Please set the JAVA_HOME variable in your environment to match the >&2
76 | echo location of your Java installation. >&2
77 | echo.
78 | goto error
79 |
80 | @REM ==== END VALIDATION ====
81 |
82 | :init
83 |
84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
85 | @REM Fallback to current working directory if not found.
86 |
87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
89 |
90 | set EXEC_DIR=%CD%
91 | set WDIR=%EXEC_DIR%
92 | :findBaseDir
93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
94 | cd ..
95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
96 | set WDIR=%CD%
97 | goto findBaseDir
98 |
99 | :baseDirFound
100 | set MAVEN_PROJECTBASEDIR=%WDIR%
101 | cd "%EXEC_DIR%"
102 | goto endDetectBaseDir
103 |
104 | :baseDirNotFound
105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
106 | cd "%EXEC_DIR%"
107 |
108 | :endDetectBaseDir
109 |
110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
111 |
112 | @setlocal EnableExtensions EnableDelayedExpansion
113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
115 |
116 | :endReadAdditionalConfig
117 |
118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
121 |
122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
123 |
124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
126 | )
127 |
128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
130 | if exist %WRAPPER_JAR% (
131 | if "%MVNW_VERBOSE%" == "true" (
132 | echo Found %WRAPPER_JAR%
133 | )
134 | ) else (
135 | if not "%MVNW_REPOURL%" == "" (
136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
137 | )
138 | if "%MVNW_VERBOSE%" == "true" (
139 | echo Couldn't find %WRAPPER_JAR%, downloading it ...
140 | echo Downloading from: %WRAPPER_URL%
141 | )
142 |
143 | powershell -Command "&{"^
144 | "$webclient = new-object System.Net.WebClient;"^
145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
147 | "}"^
148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
149 | "}"
150 | if "%MVNW_VERBOSE%" == "true" (
151 | echo Finished downloading %WRAPPER_JAR%
152 | )
153 | )
154 | @REM End of extension
155 |
156 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
157 | SET WRAPPER_SHA_256_SUM=""
158 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
159 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
160 | )
161 | IF NOT %WRAPPER_SHA_256_SUM%=="" (
162 | powershell -Command "&{"^
163 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
164 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
165 | " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
166 | " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
167 | " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
168 | " exit 1;"^
169 | "}"^
170 | "}"
171 | if ERRORLEVEL 1 goto error
172 | )
173 |
174 | @REM Provide a "standardized" way to retrieve the CLI args that will
175 | @REM work with both Windows and non-Windows executions.
176 | set MAVEN_CMD_LINE_ARGS=%*
177 |
178 | %MAVEN_JAVA_EXE% ^
179 | %JVM_CONFIG_MAVEN_PROPS% ^
180 | %MAVEN_OPTS% ^
181 | %MAVEN_DEBUG_OPTS% ^
182 | -classpath %WRAPPER_JAR% ^
183 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
184 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
185 | if ERRORLEVEL 1 goto error
186 | goto end
187 |
188 | :error
189 | set ERROR_CODE=1
190 |
191 | :end
192 | @endlocal & set ERROR_CODE=%ERROR_CODE%
193 |
194 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
195 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
196 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
197 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
198 | :skipRcPost
199 |
200 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
201 | if "%MAVEN_BATCH_PAUSE%"=="on" pause
202 |
203 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
204 |
205 | cmd /C exit /B %ERROR_CODE%
206 |
--------------------------------------------------------------------------------
/nbactions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | run
5 |
6 | jar
7 |
8 |
9 | process-classes
10 | org.codehaus.mojo:exec-maven-plugin:3.0.0:exec
11 |
12 |
13 | ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}
14 | java
15 | com.github.coderodde.util.benchmark.LinkedListBenchmark2
16 | -Xms1000m -Xmx1000m
17 | --all
18 |
19 |
20 |
21 | debug
22 |
23 | jar
24 |
25 |
26 | process-classes
27 | org.codehaus.mojo:exec-maven-plugin:3.0.0:exec
28 |
29 |
30 | ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}
31 | java
32 | true
33 | com.github.coderodde.util.benchmark.LinkedListBenchmark2
34 | <<<<<<< HEAD
35 | -Xms1000m -Xmx1000m -agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address}
36 | =======
37 | -Xms3000m -Xmx3000m -agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address}
38 | >>>>>>> e5fa429ccefb73592677fea194382d9468d6346e
39 | --all
40 |
41 |
42 |
43 | profile
44 |
45 | jar
46 |
47 |
48 | process-classes
49 | org.codehaus.mojo:exec-maven-plugin:3.0.0:exec
50 |
51 |
52 | -Xms1000m -Xmx1000m
53 | ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}
54 | com.github.coderodde.util.benchmark.LinkedListBenchmark2
55 | java
56 | --all
57 |
58 |
59 |
60 | CUSTOM-Coverage
61 | Coverage
62 |
63 | cobertura:cobertura
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | fi.helsinki
5 | IndexedLinkedList
6 | 1.618033988
7 | jar
8 |
9 | IndexedLinkedList
10 | An efficient, heuristic, indexeded linked-list for versatile usage.
11 | https://github.com/coderodde/IndexedLinkedList
12 |
13 |
14 | UTF-8
15 | 8
16 | 8
17 |
18 |
19 |
20 |
21 | MIT License
22 | http://www.opensource.org/licenses/mit-license.php
23 |
24 |
25 |
26 |
27 |
28 | Rodion Efremov
29 | rodion.efremov@helsinki.fi
30 | University of Helsinki, Department of Computer Science
31 | github.com/coderodde
32 |
33 |
34 |
35 |
36 | scm:git:git://github.com/coderodde/IndexedLinkedList.git
37 | scm:git:ssh://github.com:coderodde/IndexedLinkedList.git
38 | http://github.com/coderodde/IndexedLinkedList
39 |
40 |
41 |
42 |
43 | org.apache.logging.log4j
44 | log4j-api
45 | 2.7
46 |
47 |
48 |
49 | org.apache.logging.log4j
50 | log4j-core
51 | 2.7
52 |
53 |
54 |
55 | org.apache.logging.log4j
56 | log4j-slf4j-impl
57 | 2.7
58 |
59 |
60 |
61 | net.openhft
62 | affinity
63 | 3.23.3
64 |
65 |
66 |
67 | junit
68 | junit
69 | 4.13.2
70 | test
71 |
72 |
73 |
74 | org.hamcrest
75 | hamcrest-core
76 | 1.3
77 | test
78 |
79 |
80 |
81 | org.apache.commons
82 | commons-collections4
83 | 4.4
84 |
85 |
86 |
87 |
88 |
89 | ossrh
90 | https://oss.sonatype.org/content/repositories/snapshots
91 |
92 |
93 | ossrh
94 | https://oss.sonatype.org/service/local/staging/deploy/maven2
95 |
96 |
97 |
98 |
99 |
115 |
142 |
143 |
144 | org.apache.maven.plugins
145 | maven-surefire-plugin
146 | 3.1.2
147 |
148 |
149 |
150 | org.apache.maven.surefire
151 | surefire-junit4
152 | 3.1.2
153 |
154 |
155 |
156 |
157 |
158 | org.apache.maven.plugins
159 | maven-compiler-plugin
160 | 3.11.0
161 |
162 |
163 | -g
164 |
165 | 8
166 | 8
167 | true
168 | lines,vars,source
169 |
170 |
171 |
172 |
173 | org.apache.maven.plugins
174 | maven-source-plugin
175 | 3.3.0
176 |
177 |
178 | attach-sources
179 |
180 | jar-no-fork
181 |
182 |
183 |
184 |
185 |
186 |
187 | org.apache.maven.plugins
188 | maven-javadoc-plugin
189 | 3.6.3
190 |
191 | private
192 |
193 |
194 |
195 |
196 |
197 | com.github.coderodde.util.benchmark
198 |
199 |
200 |
201 | attach-javadocs
202 |
203 | jar
204 |
205 |
206 |
207 |
208 |
209 |
210 | org.jacoco
211 | jacoco-maven-plugin
212 | 0.8.10
213 |
214 |
215 |
216 |
217 | prepare-agent
218 |
219 |
220 |
221 |
222 | jacoco-report
223 | test
224 |
225 | report
226 |
227 |
228 |
229 |
230 |
231 |
232 | **/com/github/coderodde/util/benchmark/LinkedListBenchmarkRunner.class
233 | **/com/github/coderodde/util/benchmark/LinkedListBenchmark.class
234 | **/com/github/coderodde/util/benchmark/LinkedListBenchmark$BenchmarkChoice.class
235 | **/com/github/coderodde/util/Finger.class
236 | **/com/github/coderodde/util/Node.class
237 |
238 |
239 |
240 |
241 |
242 | org.apache.maven.plugins
243 | maven-dependency-plugin
244 | 3.6.0
245 |
246 |
247 | copy-dependencies
248 | prepare-package
249 |
250 | copy-dependencies
251 |
252 |
253 |
254 | ${project.build.directory}/libs
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 | org.apache.maven.plugins
263 | maven-jar-plugin
264 | 3.3.0
265 |
266 |
267 |
268 | true
269 | libs/
270 |
271 | com.github.coderodde.util.benchmark.LinkedListBenchmark2
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
--------------------------------------------------------------------------------
/src/main/java/com/github/coderodde/util/Finger.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2024 rodio.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package com.github.coderodde.util;
25 |
26 | import java.util.Objects;
27 |
28 | /**
29 | * This static inner class implements the actual finger.
30 | *
31 | * @param the type of the list's satellite data.
32 | */
33 | final class Finger {
34 |
35 | /**
36 | * The pointed to {@link Node}.
37 | */
38 | Node node;
39 |
40 | /**
41 | * The index at which the {@code node} appears in the list.
42 | */
43 | int index;
44 |
45 | /**
46 | * Constructs a new {@link Finger}.
47 | *
48 | * @param node the pointed node.
49 | * @param index the index of {@code node} in the actual list.
50 | */
51 | Finger(Node node, int index) {
52 | this.node = node;
53 | this.index = index;
54 | }
55 |
56 | /**
57 | * Copy constructs this finger.
58 | *
59 | * @param finger the finger whose state to copy.
60 | */
61 | Finger(Finger finger) {
62 | this.node = finger.node;
63 | this.index = finger.index;
64 | }
65 |
66 | /**
67 | * Returns the index of this finger. Used for research.
68 | *
69 | * @return the index of this finger.
70 | */
71 | public int getIndex() {
72 | return index;
73 | }
74 |
75 | @Override
76 | public boolean equals(Object o) {
77 | if (o == null) {
78 | return false;
79 | }
80 |
81 | if (o == this) {
82 | return true;
83 | }
84 |
85 | if (!o.getClass().equals(this.getClass())) {
86 | return false;
87 | }
88 |
89 | final Finger other = (Finger) o;
90 |
91 | return Objects.equals(index, other.index)
92 | && Objects.equals(node, other.node);
93 | }
94 |
95 | /**
96 | * Returns the textual representation of this finger.
97 | *
98 | * @return the textual representation.
99 | */
100 | @Override
101 | public String toString() {
102 | return "[Finger; index = " + index
103 | + ", item = " + ((node == null) ? "null" : node.item)
104 | + "]";
105 | }
106 |
107 | /**
108 | * Moves this finger {@code steps} position to the left.
109 | *
110 | * @param steps the number of steps to rewind.
111 | */
112 | void rewindLeft(int steps) {
113 | for (int i = 0; i < steps; i++) {
114 | node = node.prev;
115 | }
116 |
117 | index -= steps;
118 | }
119 |
120 | /**
121 | * Moves this finger {@code steps} position to the right.
122 | *
123 | * @param steps the number of steps to rewind.
124 | */
125 | void rewindRight(int steps) {
126 | for (int i = 0; i < steps; i++) {
127 | node = node.next;
128 | }
129 |
130 | index += steps;
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/main/java/com/github/coderodde/util/FingerList.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2025 Rodion Efremov
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package com.github.coderodde.util;
25 |
26 | import java.util.Arrays;
27 | import java.util.Objects;
28 |
29 | /**
30 | * This inner class implements the finger list data structure for managing list
31 | * fingers.
32 | *
33 | * @param the list node item type.
34 | */
35 | final class FingerList {
36 |
37 | /**
38 | * The owner indexed linked list.
39 | */
40 | private final IndexedLinkedList list;
41 |
42 | /**
43 | * This is also the minimum capacity.
44 | */
45 | private static final int INITIAL_CAPACITY = 8;
46 |
47 | /**
48 | * The actual list storage array.
49 | */
50 | Finger[] fingerArray = new Finger[INITIAL_CAPACITY];
51 |
52 | /**
53 | * Constructs this finger list setting it to empty.
54 | *
55 | * @param list the owner list.
56 | */
57 | FingerList(IndexedLinkedList list) {
58 | this.list = list;
59 | this.fingerArray[0] = new Finger<>(null, 0);
60 | }
61 |
62 | @Override
63 | public boolean equals(Object o) {
64 | if (o == null) {
65 | return false;
66 | }
67 |
68 | if (o == this) {
69 | return true;
70 | }
71 |
72 | if (!o.getClass().equals(this.getClass())) {
73 | return false;
74 | }
75 |
76 | final FingerList other = (FingerList) o;
77 |
78 | if (size != other.size) {
79 | return false;
80 | }
81 |
82 | for (int i = 0; i < size; i++) {
83 | if (!Objects.equals(fingerArray[i], other.fingerArray[i])) {
84 | return false;
85 | }
86 | }
87 |
88 | return true;
89 | }
90 |
91 | @Override
92 | public String toString() {
93 | StringBuilder sb = new StringBuilder().append("[");
94 | boolean first = true;
95 |
96 | for (int i = 0; i != size; i++) {
97 | if (first) {
98 | first = false;
99 | } else {
100 | sb.append(", ");
101 | }
102 |
103 | sb.append(fingerArray[i].toString());
104 | }
105 |
106 | return sb.append("]").toString();
107 | }
108 |
109 | /**
110 | * The number of fingers stored in the list. This field does not count the
111 | * end-of-list sentinel finger {@code F} for which {@code F.index = size}.
112 | */
113 | int size;
114 |
115 | /**
116 | * Adjusts the finger list after removing the first finger.
117 | */
118 | void adjustOnRemoveFirst() {
119 | int lastPrefixIndex = Integer.MAX_VALUE;
120 |
121 | for (int i = 0; i < size; ++i) {
122 | Finger finger = fingerArray[i];
123 |
124 | if (finger.index != i) {
125 | lastPrefixIndex = i;
126 | break;
127 | } else {
128 | finger.node = finger.node.next;
129 | }
130 | }
131 |
132 | shiftFingerIndicesToLeftOnceAll(lastPrefixIndex);
133 | }
134 |
135 | /**
136 | * Appends the input finger to the tail of the finger list.
137 | *
138 | * @param finger the finger to append.
139 | */
140 | void appendFinger(Finger finger) {
141 | size++;
142 | enlargeFingerArrayIfNeeded(size + 1);
143 | fingerArray[size] = fingerArray[size - 1];
144 | fingerArray[size - 1] = finger;
145 | fingerArray[size].index = list.size;
146 | }
147 |
148 | /**
149 | * Pushes {@code numberOfFingersToMoveToPrefix} fingers to the prefix with
150 | * {@code numberOfFingersInPrefix} fingers.
151 | *
152 | * @param fromIndex the index of the leftmost element to
153 | * remove.
154 | * @param numberOfFingersInPrefix the number of fingers already in the
155 | * prefix.
156 | * @param numberOfFingersToMoveToPrefix the number of fingers we need to
157 | * move to the prefix.
158 | */
159 | void arrangePrefix(int fromIndex,
160 | int numberOfFingersInPrefix,
161 | int numberOfFingersToMoveToPrefix) {
162 |
163 | makeRoomAtPrefix(fromIndex,
164 | numberOfFingersInPrefix,
165 | numberOfFingersToMoveToPrefix);
166 |
167 | pushCoveredFingersToPrefix(fromIndex,
168 | numberOfFingersInPrefix,
169 | numberOfFingersToMoveToPrefix);
170 | }
171 |
172 | void arrangeSuffix(int toIndex,
173 | int toFingerIndex,
174 | int numberOfSuffixFingers,
175 | int numberOfFingetsToPush) {
176 |
177 | makeRoomAtSuffix(toIndex,
178 | toFingerIndex,
179 | numberOfSuffixFingers,
180 | numberOfFingetsToPush);
181 |
182 | pushCoveredFingersToSuffix(toIndex,
183 | numberOfSuffixFingers,
184 | numberOfFingetsToPush);
185 | }
186 |
187 | /**
188 | * Clears entirely this finger list. Only the end-of-finger-list finger
189 | * remains in the finger list. Not {@code private} since is used in the unit
190 | * tests.
191 | */
192 | void clear() {
193 | Arrays.fill(fingerArray, 0, size, null);
194 | fingerArray = new Finger[INITIAL_CAPACITY];
195 | fingerArray[0] = new Finger<>(null, 0);
196 | size = 0;
197 | }
198 |
199 | /**
200 | * Contracts the finger array, if possible. The {@code nextSize} defines the
201 | * requested finger array size not counting the end-of-finger-list sentinel
202 | * finger.
203 | *
204 | * @param nextSize the requested size not counting the end-of-finger-list
205 | * sentinel finger.
206 | */
207 | void contractFingerArrayIfNeeded(int nextSize) {
208 | // Can we contract at least once?
209 | if ((nextSize + 1) * 4 < fingerArray.length
210 | && fingerArray.length > INITIAL_CAPACITY) {
211 |
212 | int nextCapacity = fingerArray.length / 2;
213 |
214 | // Good, we can. But can we keep on splitting in half the
215 | // capacity any further?
216 | while (nextCapacity >= 2 * (nextSize + 1)
217 | && nextCapacity > INITIAL_CAPACITY) {
218 | // Yes, we can do it as well.
219 | nextCapacity /= 2;
220 | }
221 |
222 | fingerArray = Arrays.copyOf(fingerArray, nextCapacity);
223 | }
224 | }
225 |
226 | /**
227 | * Enlarges the finger array so that it can accommodate
228 | * {@code requestedSize} fingers.
229 | *
230 | * @param requestedSize the requested size, including the end-of-finger-list
231 | * sentinel finger.
232 | */
233 | private void enlargeFingerArrayIfNeeded(int requestedSize) {
234 | int nextCapacity = fingerArray.length;
235 |
236 | while (requestedSize > nextCapacity) {
237 | nextCapacity *= 2;
238 | }
239 |
240 | if (nextCapacity != fingerArray.length) {
241 | fingerArray = Arrays.copyOf(fingerArray, nextCapacity);
242 | }
243 | }
244 |
245 | /**
246 | * Returns {@code index}th finger.
247 | *
248 | * @param index the index of the target finger.
249 | * @return the {@code index}th finger.
250 | */
251 | Finger getFinger(int index) {
252 | return fingerArray[index];
253 | }
254 |
255 | /**
256 | * Returns the index of the finger that is closest to the
257 | * {@code elementIndex}th list element.
258 | *
259 | * @param elementIndex the target element index.
260 | * @return the index of the finger that is closest to the
261 | * {@code elementIndex}th element.
262 | */
263 | int getClosestFingerIndex(int elementIndex) {
264 | return normalize(getFingerIndexImpl(elementIndex), elementIndex);
265 | }
266 |
267 | /**
268 | * Returns the finger index {@code i}, such that
269 | * {@code fingerArray[i].index} is no less than {@code elementIndex}, and
270 | * {@code fingerArray[i].index} is closest to {@code elementIndex}. This
271 | * algorithm is translated from
272 | * C++
273 | * lower_bound
algorithm.
274 | *
275 | * @param elementIndex the target element index.
276 | * @return the index of the finger {@code f}, for which
277 | * {@code elementIndex <= f.index} and {@code f} is the leftmost such
278 | * finger.
279 | */
280 | int getFingerIndexImpl(int elementIndex) {
281 | int count = size + 1; // + 1 for the end sentinel.
282 | int idx = 0;
283 |
284 | while (count > 0) {
285 | int it = idx;
286 | int step = count / 2;
287 | it += step;
288 |
289 | if (fingerArray[it].index < elementIndex) {
290 | idx = ++it;
291 | count -= step + 1;
292 | } else {
293 | count = step;
294 | }
295 | }
296 |
297 | return idx;
298 | }
299 |
300 | /**
301 | * Access the {@code index}th node without modifying the fingers unlike
302 | * {@link #getNode(int)}.
303 | *
304 | * @param index the index of the desired node.
305 | *
306 | * @return the {@code index}th node.
307 | */
308 | Node getNodeNoFingersFix(int index) {
309 | Finger finger = fingerArray[getClosestFingerIndex(index)];
310 | int steps = finger.index - index;
311 | Node node = finger.node;
312 |
313 | if (steps > 0) {
314 | for (int i = 0; i < steps; i++) {
315 | node = node.prev;
316 | }
317 | } else {
318 | for (int i = 0; i < -steps; i++) {
319 | node = node.next;
320 | }
321 | }
322 |
323 | return node;
324 | }
325 |
326 | /**
327 | * Returns the {@code i}th node of this linked list. The closest finger is
328 | * updated to point to the returned node.
329 | *
330 | * @param elementIndex the element index.
331 | * @return the {@code index}th node in the linked list.
332 | */
333 | Node getNode(int elementIndex) {
334 | if (size < 3) {
335 | // We need at least 3 fingers to do the actual trick:
336 | return list.getNodeSequentially(elementIndex);
337 | }
338 |
339 | int fingerIndex = getFingerIndexImpl(elementIndex);
340 |
341 | if (fingerIndex == 0) {
342 | // There is no required preceding finger:
343 | return getPrefixNode(elementIndex);
344 | }
345 |
346 | if (fingerIndex >= size - 1) {
347 | return getSuffixNode(elementIndex);
348 | }
349 |
350 | Finger a = fingerArray[fingerIndex - 1];
351 | Finger b = fingerArray[fingerIndex];
352 | Finger c = fingerArray[fingerIndex + 1];
353 |
354 | int diff = c.index - a.index;
355 | int step = diff / 2;
356 | int saveBIndex = b.index;
357 | int nextBIndex = a.index + step;
358 |
359 | b.index = nextBIndex;
360 |
361 | // Rewind the finger b node:
362 | if (saveBIndex < nextBIndex) {
363 | for (int i = 0; i != nextBIndex - saveBIndex; i++) {
364 | b.node = b.node.next;
365 | }
366 | } else {
367 | // Here, 'saveBIndex >= nextBIndex':
368 | for (int i = 0; i != saveBIndex - nextBIndex; i++) {
369 | b.node = b.node.prev;
370 | }
371 | }
372 |
373 | // Go fetch the correct node:
374 | if (elementIndex < nextBIndex) {
375 | // Here, the desired element is between a and b:
376 | int leftDistance = elementIndex - a.index;
377 | int rightDistance = b.index - elementIndex;
378 |
379 | if (leftDistance < rightDistance) {
380 | Node node = a.node;
381 |
382 | for (int i = 0; i != leftDistance; i++) {
383 | node = node.next;
384 | }
385 |
386 | return node;
387 | } else {
388 | Node node = b.node;
389 | // TODO: Replace saveBIndex - elementIndex with rightDistance?
390 | for (int i = 0; i != rightDistance; i++) {
391 | node = node.prev;
392 | }
393 |
394 | return node;
395 | }
396 | } else {
397 | // Here, the desired element is between c and b:
398 | int leftDistance = elementIndex - b.index;
399 | int rightDistance = c.index - elementIndex;
400 |
401 | if (leftDistance < rightDistance) {
402 | Node node = b.node;
403 |
404 | for (int i = 0; i != leftDistance; i++) {
405 | node = node.next;
406 | }
407 |
408 | return node;
409 | } else {
410 | Node node = c.node;
411 |
412 | for (int i = 0; i != rightDistance; i++) {
413 | node = node.prev;
414 | }
415 |
416 | return node;
417 | }
418 | }
419 | }
420 |
421 | /**
422 | * Normalizes the first finger and returns the {@code elementIndex}th node.
423 | *
424 | * @param elementIndex the index of the desired element.
425 | *
426 | * @return the node corresponding to the {@code elementIndex}th position.
427 | */
428 | private Node getPrefixNode(int elementIndex) {
429 | Finger a = fingerArray[0];
430 | Finger b = fingerArray[1];
431 | Node aNode = a.node;
432 |
433 | // Put a between b and the beginning of the list:
434 | int nextAIndex = b.index / 2;
435 | int saveAIndex = a.index;
436 |
437 | a.index = nextAIndex;
438 |
439 | if (saveAIndex < nextAIndex) {
440 | // Here, we need to rewind to the right:
441 | for (int i = saveAIndex; i < nextAIndex; i++) {
442 | aNode = aNode.next;
443 | }
444 | } else {
445 | // Once here, 'saveAIndex >= nextAIndex'.
446 | // We need to rewind to the left:
447 | for (int i = nextAIndex; i < saveAIndex; i++) {
448 | aNode = aNode.prev;
449 | }
450 | }
451 |
452 | a.node = aNode;
453 |
454 | // Go get the proper node:
455 | if (elementIndex < nextAIndex) {
456 | // Here, the desired element is between the head of the list and
457 | // the very first figner:
458 | int leftDistance = elementIndex;
459 | int rightDistance = nextAIndex - elementIndex;
460 |
461 | if (leftDistance < rightDistance) {
462 | Node node = (Node) list.head;
463 |
464 | for (int i = 0; i != elementIndex; i++) {
465 | node = node.next;
466 | }
467 |
468 | return node;
469 | } else {
470 | Node node = aNode;
471 |
472 | for (int i = 0; i != rightDistance; i++) {
473 | node = node.prev;
474 | }
475 |
476 | return node;
477 | }
478 | } else {
479 | return aNode;
480 | }
481 | }
482 |
483 | /**
484 | * Returns the {@code elementIndex}th node and normalizes the last finger.
485 | *
486 | * @param elementIndex the index of the desired element.
487 | *
488 | * @return the {@code elementIndex}th node.
489 | */
490 | private Node getSuffixNode(int elementIndex) {
491 | Finger a = fingerArray[size - 2];
492 | Finger b = fingerArray[size - 1];
493 | Node bNode = b.node;
494 |
495 | int saveBIndex = b.index;
496 | int nextBIndex = (a.index + list.size) / 2;
497 |
498 | b.index = nextBIndex;
499 |
500 | // Rewind the finger 'b' to between 'a' and tail:
501 | if (saveBIndex < nextBIndex) {
502 | int distance = nextBIndex - saveBIndex;
503 |
504 | for (int i = 0; i != distance; i++) {
505 | bNode = bNode.next;
506 | }
507 | } else {
508 | // Here, 'nextBIndex <= saveBIndex':
509 | int distance = saveBIndex - nextBIndex;
510 |
511 | for (int i = 0; i != distance; i++) {
512 | bNode = bNode.prev;
513 | }
514 | }
515 |
516 | b.node = bNode;
517 |
518 | // Go get the proper node:
519 | if (elementIndex < nextBIndex) {
520 | // Here, the desired element node is between 'a' and 'b':
521 | int leftDistance = elementIndex - a.index;
522 | int rightDistance = nextBIndex - elementIndex;
523 |
524 | if (leftDistance < rightDistance) {
525 | Node node = a.node;
526 |
527 | for (int i = 0; i != leftDistance; i++) {
528 | node = node.next;
529 | }
530 |
531 | return node;
532 | } else {
533 | Node node = b.node;
534 |
535 | for (int i = 0; i != rightDistance; i++) {
536 | node = node.prev;
537 | }
538 |
539 | return node;
540 | }
541 | } else {
542 | // Here, the desired element node is between 'b' and the tail
543 | // node of the list:
544 | int leftDistance = elementIndex - nextBIndex;
545 | int rightDistance = list.size - elementIndex - 1;
546 |
547 | if (leftDistance < rightDistance) {
548 | // Once here, rewind the node reference from bNode to the
549 | // right:
550 | Node node = bNode;
551 |
552 | for (int i = 0; i != leftDistance; i++) {
553 | node = node.next;
554 | }
555 |
556 | return node;
557 | } else {
558 | // Once here, rewind the node reference from tail to the
559 | // left:
560 | Node node = list.tail;
561 |
562 | for (int i = 0; i != rightDistance; i++) {
563 | node = node.prev;
564 | }
565 |
566 | return node;
567 | }
568 | }
569 | }
570 |
571 | /**
572 | * Inserts the input finger into the finger list such that the entire finger
573 | * list is sorted by indices.
574 | *
575 | * @param finger the finger to insert.
576 | */
577 | void insertFingerAndShiftOnceToRight(Finger finger) {
578 | enlargeFingerArrayIfNeeded(size + 2);
579 | int beforeFingerIndex = getFingerIndexImpl(finger.index);
580 | System.arraycopy(
581 | fingerArray,
582 | beforeFingerIndex,
583 | fingerArray,
584 | beforeFingerIndex + 1,
585 | size + 1 - beforeFingerIndex);
586 |
587 | ++size;
588 |
589 | // Shift fingerArray[beforeFingerIndex + 1 ... size] one position to
590 | // the right (towards larger index values):
591 | shiftFingerIndicesToRightOnce(beforeFingerIndex + 1);
592 | fingerArray[beforeFingerIndex] = finger;
593 | }
594 |
595 | /**
596 | * Make sure we can insert {@code roomSize} fingers starting from
597 | * {@code fingerIndex}, shifting all the fingers starting from
598 | * {@code numberOfNodes} to the right.
599 | *
600 | * @param fingerIndex the finger index of the first finger in the shifted
601 | * finger slice.
602 | * @param roomSize the number of free spots requested.
603 | * @param numberOfNodes the shift amount of the moved fingers.
604 | */
605 | void makeRoomAtIndex(int fingerIndex,
606 | int roomSize,
607 | int numberOfNodes) {
608 |
609 | shiftFingerIndicesToRight(fingerIndex, numberOfNodes);
610 | size += roomSize;
611 | enlargeFingerArrayIfNeeded(size + 1); // +1 for the end of list
612 | // sentinel.
613 | System.arraycopy(fingerArray,
614 | fingerIndex,
615 | fingerArray,
616 | fingerIndex + roomSize,
617 | size - roomSize - fingerIndex + 1);
618 | }
619 |
620 | void makeRoomAtPrefix(int fromIndex,
621 | int numberOfFingersInPrefix,
622 | int numberOfFingersToMoveToPrefix) {
623 |
624 | if (numberOfFingersToMoveToPrefix <= 0) {
625 | // System.out.println("no: " + numberOfFingersToMoveToPrefix);
626 | // return;
627 | }
628 |
629 | if (numberOfFingersInPrefix == 0) {
630 | // Here, no fingers in the prefix to move.
631 | return;
632 | }
633 |
634 | int targetFingerIndex = numberOfFingersInPrefix - 1;
635 | int freeFingerSpotsSoFar = fromIndex
636 | - getFinger(targetFingerIndex).index
637 | - 1;
638 |
639 | if (freeFingerSpotsSoFar >= numberOfFingersToMoveToPrefix) {
640 | return;
641 | }
642 |
643 | for (; targetFingerIndex > 0; targetFingerIndex--) {
644 | Finger finger1 = getFinger(targetFingerIndex - 1);
645 | Finger finger2 = getFinger(targetFingerIndex);
646 |
647 | int distance = finger2.index
648 | - finger1.index
649 | - 1;
650 |
651 | freeFingerSpotsSoFar += distance;
652 |
653 | if (freeFingerSpotsSoFar >= numberOfFingersToMoveToPrefix) {
654 | break;
655 | }
656 | }
657 |
658 | if (freeFingerSpotsSoFar < numberOfFingersToMoveToPrefix) {
659 | // Once here, we need to move the leftmost prefix finger to the
660 | // left.
661 | int index = fromIndex
662 | - numberOfFingersInPrefix
663 | - numberOfFingersToMoveToPrefix;
664 |
665 | Node node = getNodeNoFingersFix(index);
666 |
667 | for (int i = 0; i < numberOfFingersInPrefix; i++) {
668 | Finger finger = getFinger(i);
669 | finger.index = index++;
670 | finger.node = node;
671 | node = node.next;
672 | }
673 | } else {
674 | Finger startFinger = getFinger(targetFingerIndex - 1);
675 | int index = startFinger.index;
676 | Node node = startFinger.node;
677 |
678 | for (int i = targetFingerIndex; i < numberOfFingersInPrefix; i++) {
679 | Finger finger = getFinger(i);
680 | node = node.next;
681 | finger.node = node;
682 | finger.index = ++index;
683 | }
684 | }
685 | }
686 |
687 | void makeRoomAtSuffix(int toIndex,
688 | int toFingerIndex,
689 | int numberOfFingersInSuffix,
690 | int numberOfFingersToMoveToSuffix) {
691 |
692 | if (numberOfFingersInSuffix == 0) {
693 | // Here, no fingers in the suffix to move.
694 | return;
695 | }
696 |
697 | int targetFingerIndex = size - numberOfFingersInSuffix;
698 | int freeFingerSpotsSoFar = getFinger(targetFingerIndex).index
699 | - toIndex;
700 |
701 | if (freeFingerSpotsSoFar >= numberOfFingersToMoveToSuffix) {
702 | return;
703 | }
704 |
705 | for (; targetFingerIndex < size - 1; targetFingerIndex++) {
706 | Finger finger1 = getFinger(targetFingerIndex);
707 | Finger finger2 = getFinger(targetFingerIndex + 1);
708 |
709 | int distance = finger2.index
710 | - finger1.index
711 | - 1;
712 |
713 | freeFingerSpotsSoFar += distance;
714 |
715 | if (freeFingerSpotsSoFar >= numberOfFingersToMoveToSuffix) {
716 | break;
717 | }
718 | }
719 |
720 | if (freeFingerSpotsSoFar < numberOfFingersToMoveToSuffix) {
721 | // Once here, we need to move the rightmost suffix finger to the
722 | // right.
723 | int index = list.size
724 | - numberOfFingersInSuffix;
725 |
726 | Node node = getNodeNoFingersFix(index);
727 |
728 | for (int i = 0; i < numberOfFingersInSuffix; i++) {
729 | Finger finger =
730 | getFinger(size - numberOfFingersInSuffix + i);
731 |
732 | finger.index = index++;
733 | finger.node = node;
734 | node = node.next;
735 | }
736 | } else {
737 | Finger startFinger = getFinger(targetFingerIndex + 1);
738 | int index = startFinger.index - 1;
739 | Node node = startFinger.node.prev;
740 |
741 | // TODO: Debug, please!
742 | for (int i = targetFingerIndex;
743 | i >= toFingerIndex;
744 | i--) {
745 | Finger finger = getFinger(i);
746 | finger.index = index--;
747 | finger.node = node;
748 | node = node.prev;
749 | }
750 | }
751 | }
752 |
753 | /**
754 | * Makes sure that the returned finger index {@code i} points to the closest
755 | * finger in the finger array.
756 | *
757 | * @param fingerIndex the finger index.
758 | * @param elementIndex the element index.
759 | *
760 | * @return the index of the finger that is closest to the
761 | * {@code elementIndex}th element.
762 | */
763 | private int normalize(int fingerIndex, int elementIndex) {
764 | if (fingerIndex == 0) {
765 | // Since we cannot point to '-1'th finger, return 0:
766 | return 0;
767 | }
768 |
769 | if (fingerIndex == size) {
770 | // Don't go outside of 'size - 1':
771 | return size - 1;
772 | }
773 |
774 | Finger finger1 = fingerArray[fingerIndex - 1];
775 | Finger finger2 = fingerArray[fingerIndex];
776 |
777 | int distance1 = elementIndex - finger1.index;
778 | int distance2 = finger2.index - elementIndex;
779 |
780 | // Return the closest finger index:
781 | return distance1 < distance2 ? fingerIndex - 1 : fingerIndex;
782 | }
783 |
784 | /**
785 | * Creates a finger for the input node {@code node} and inserts it at the
786 | * head of the finger array.
787 | *
788 | * @param node the target node.
789 | */
790 | void prependFingerForNode(Node node) {
791 | Finger finger = new Finger<>(node, 0);
792 | enlargeFingerArrayIfNeeded(size + 2);
793 | shiftFingerIndicesToRightOnce(0);
794 | System.arraycopy(fingerArray, 0, fingerArray, 1, size + 1);
795 | fingerArray[0] = finger;
796 | size++;
797 | }
798 |
799 | /**
800 | * Pushes {@code numberOfFingersToPush} to the finger prefix.
801 | *
802 | * @param fromIndex the starting index of the range to delete.
803 | * @param numberOfPrefixFingers the number of fingers in the prefix.
804 | * @param numberOfFingersToPush the number of fingers to move to the prefix.
805 | */
806 | void pushCoveredFingersToPrefix(int fromIndex,
807 | int numberOfPrefixFingers,
808 | int numberOfFingersToPush) {
809 | if (numberOfPrefixFingers == 0) {
810 | int index = fromIndex - 1;
811 | Node node = getNodeNoFingersFix(index);
812 |
813 | for (int i = numberOfFingersToPush - 1; i >= 0; i--) {
814 | Finger finger = getFinger(i);
815 | finger.index = index--;
816 | finger.node = node;
817 | node = node.prev;
818 | }
819 | } else {
820 | Finger rightmostPrefixFinger =
821 | getFinger(numberOfPrefixFingers - 1);
822 |
823 | int index = rightmostPrefixFinger.index + 1;
824 | Node node = rightmostPrefixFinger.node.next;
825 |
826 | for (int i = numberOfPrefixFingers;
827 | i < numberOfPrefixFingers + numberOfFingersToPush;
828 | i++) {
829 |
830 | Finger finger = getFinger(i);
831 | finger.index = index++;
832 | finger.node = node;
833 | node = node.next;
834 | }
835 | }
836 | }
837 |
838 | void pushCoveredFingersToSuffix(int toIndex,
839 | int numberOfSuffixFingers,
840 | int numberOfFingersToPush) {
841 | if (numberOfSuffixFingers == 0) {
842 | int index = toIndex;
843 | Node node = getNodeNoFingersFix(index);
844 |
845 | for (int i = 0; i < numberOfFingersToPush; i++) {
846 | Finger finger = getFinger(size - numberOfFingersToPush + i);
847 | finger.index = index++;
848 | finger.node = node;
849 | node = node.next;
850 | }
851 | } else {
852 | Finger leftmostSuffixFinger =
853 | getFinger(size - numberOfSuffixFingers);
854 |
855 | int index = leftmostSuffixFinger.index;
856 | Node node = leftmostSuffixFinger.node;
857 |
858 | // TODO: Check this bound!
859 | for (int i = 0; i < numberOfFingersToPush; i++) {
860 | Finger finger =
861 | getFinger(size - numberOfSuffixFingers - 1 - i);
862 |
863 | node = node.prev;
864 | finger.node = node;
865 | finger.index = --index;
866 | }
867 | }
868 | }
869 |
870 | /**
871 | * Removes the last finger residing right before the end-of-finger-list
872 | * sentinel finger.
873 | */
874 | void removeFinger() {
875 | contractFingerArrayIfNeeded(--size);
876 | fingerArray[size] = fingerArray[size + 1];
877 | fingerArray[size + 1] = null;
878 | fingerArray[size].index = list.size;
879 | }
880 |
881 | void removeSingleNode(int fromIndex) {
882 |
883 | }
884 |
885 | // TODO: Debug me, please!
886 | void removeFingersOnDeleteRange(int fromFingerIndex,
887 | int numberOfFingersToRemove,
888 | int removalRangeLength) {
889 |
890 | if (numberOfFingersToRemove != 0) {
891 |
892 | System.arraycopy(
893 | fingerArray,
894 | fromFingerIndex
895 | + list.numberOfCoveringFingersToPrefix
896 | + numberOfFingersToRemove,
897 | fingerArray,
898 | fromFingerIndex + list.numberOfCoveringFingersToPrefix,
899 | size
900 | - fromFingerIndex
901 | - numberOfFingersToRemove
902 | - list.numberOfCoveringFingersToPrefix
903 | + 1);
904 |
905 | Arrays.fill(fingerArray,
906 | size - numberOfFingersToRemove + 1,
907 | size + 1,
908 | null);
909 |
910 | this.size -= numberOfFingersToRemove;
911 | shiftFingerIndicesToLeft(
912 | fromFingerIndex + list.numberOfCoveringFingersToPrefix,
913 | removalRangeLength);
914 | } else {
915 | shiftFingerIndicesToLeft(
916 | fromFingerIndex + list.numberOfCoveringFingersToPrefix,
917 | removalRangeLength);
918 | }
919 |
920 | // shiftFingerIndicesToLeft(
921 | // fromFingerIndex + list.numberOfCoveringFingersToPrefix,
922 | // removalRangeLength);
923 |
924 | this.list.size -= removalRangeLength;
925 | }
926 |
927 | /**
928 | * Sets the finger {@code finger} to the finger array at index
929 | * {@code index}.
930 | *
931 | * @param index the index of the finger list component.
932 | * @param finger the target finger to set.
933 | */
934 | void setFinger(int index, Finger finger) {
935 | fingerArray[index] = finger;
936 | }
937 |
938 | /**
939 | * Sets all the leftmost {@code indices.length} fingers to the specified
940 | * indices.
941 | *
942 | * @param indices the target indices.
943 | */
944 | void setFingerIndices(int... indices) {
945 | Arrays.sort(indices);
946 | int fingerIndex = 0;
947 |
948 | for (final int index : indices) {
949 | final Finger finger = fingerArray[fingerIndex++];
950 | finger.index = index;
951 | finger.node = getNodeSequentially(index);
952 | }
953 | }
954 |
955 | /**
956 | * Accesses the {@code index}th node sequentially without using fingers and
957 | * modifying the fingers.
958 | *
959 | * @param index the index of the desired node.
960 | *
961 | * @return {@code index} node.
962 | */
963 | private Node getNodeSequentially(final int index) {
964 | return list.getNodeSequentially(index);
965 | }
966 |
967 | /**
968 | * Moves all the fingers in range {@code [startFingerIndex, size]}
969 | * {@code shiftLength} positions to the left (towards smaller indices).
970 | *
971 | * @param startFingerIndex the index of the leftmost finger to shift.
972 | * @param shiftLength the length of the shift operation.
973 | */
974 | void shiftFingerIndicesToLeft(int startFingerIndex, int shiftLength) {
975 | for (int i = startFingerIndex; i <= size; ++i) {
976 | fingerArray[i].index -= shiftLength;
977 | }
978 | }
979 |
980 | /**
981 | * Moves all the fingers in range {@code [startFingerIndex, size]} one
982 | * position to the left (towards smaller indices).
983 | *
984 | * @param startFingerIndex the index of the leftmost finger to shift.
985 | */
986 | void shiftFingerIndicesToLeftOnceAll(int startFingerIndex) {
987 | for (int i = startFingerIndex; i <= size; ++i) {
988 | fingerArray[i].index--;
989 | }
990 | }
991 |
992 | /**
993 | * Moves all the fingers in range {@code [startFingerIndex, size]}
994 | * {@code shiftLength} positions to the right (towards larger indices).
995 | *
996 | * @param startIndex the index of the leftmost finger to shift.
997 | * @param shiftLength the length of the shift operation.
998 | */
999 | void shiftFingerIndicesToRight(int startIndex, int shiftLength) {
1000 | for (int i = startIndex; i <= size; ++i) {
1001 | fingerArray[i].index += shiftLength;
1002 | }
1003 | }
1004 |
1005 | /**
1006 | * Moves all the fingers in range {@code [startFingerIndex, size]} one
1007 | * position to the right (towards larger indices).
1008 | *
1009 | * @param startIndex the index of the leftmost finger to shift.
1010 | */
1011 | void shiftFingerIndicesToRightOnce(int startIndex) {
1012 | shiftFingerIndicesToRight(startIndex, 1);
1013 | }
1014 |
1015 | /**
1016 | * Returns the number of fingers in this finger list not counting the
1017 | * end-of-finger-list finger.
1018 | *
1019 | * @return the number of fingers in this finger list.
1020 | */
1021 | int size() {
1022 | return size;
1023 | }
1024 | }
1025 |
--------------------------------------------------------------------------------
/src/main/java/com/github/coderodde/util/Node.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2024 rodio.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package com.github.coderodde.util;
25 |
26 | import java.util.Objects;
27 |
28 | /**
29 | * This static inner class implements the actual linked list node.
30 | *
31 | * @param the type of the satellite data.
32 | */
33 | final class Node {
34 |
35 | /**
36 | * The actual satellite datum.
37 | */
38 | E item;
39 |
40 | /**
41 | * The previous node or {@code null} if this {@link Node} is the head of the
42 | * list.
43 | */
44 | Node prev;
45 |
46 | /**
47 | * The next node or {@code null} if this {@link Node} is the tail of the
48 | * list.
49 | */
50 | Node next;
51 |
52 | /**
53 | * Constructs a new {@link Node} object.
54 | *
55 | * @param item the satellite datum of the newly created {@link Node}.
56 | */
57 | Node(E item) {
58 | this.item = item;
59 | }
60 |
61 | /**
62 | * Returns {@code true} if and only if the input object is another node with
63 | * the same item.
64 | *
65 | * @param o the object to test.
66 | *
67 | * @return {@code true} iff the two nodes are the same.
68 | */
69 | @Override
70 | public boolean equals(Object o) {
71 | if (o == null) {
72 | return false;
73 | }
74 |
75 | if (o == this) {
76 | return true;
77 | }
78 |
79 | if (!getClass().equals(o.getClass())) {
80 | return false;
81 | }
82 |
83 | final Node other = (Node) o;
84 | return Objects.equals(this.item, other.item);
85 | }
86 |
87 | /**
88 | * Returns the textual representation of this {@link Node}.
89 | *
90 | * @return the textual representation.
91 | */
92 | @Override
93 | public String toString() {
94 | return "[Node; item = " + item + "]";
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/java/com/github/coderodde/util/benchmark/LinkedListBenchmark.java:
--------------------------------------------------------------------------------
1 | package com.github.coderodde.util.benchmark;
2 |
3 | import com.github.coderodde.util.IndexedLinkedList;
4 | import java.util.ArrayList;
5 | import java.util.Collection;
6 | import java.util.Collections;
7 | import java.util.Deque;
8 | import java.util.HashSet;
9 | import java.util.Iterator;
10 | import java.util.LinkedList;
11 | import java.util.List;
12 | import java.util.ListIterator;
13 | import java.util.Random;
14 | import java.util.stream.Collectors;
15 | import org.apache.commons.collections4.list.TreeList;
16 |
17 | final class LinkedListBenchmark {
18 |
19 | private static final int ADD_FIRST_OPERATIONS = 100_000;
20 | private static final int ADD_LAST_OPERATIONS = 100_000;
21 | private static final int ADD_AT_OPERATIONS = 10_000;
22 | private static final int ADD_COLLECTION_AT_OPERATIONS = 4_000;
23 | private static final int ADD_LAST_COLLECTION_OPERATIONS = 10_000;
24 | private static final int REMOVE_VIA_INDEX_OPERATIONS = 10_000;
25 | private static final int REMOVE_OBJECT_OPERATIONS = 5_000;
26 | private static final int GET_OPERATIONS = 5_000;
27 | private static final int REMOVE_FIRST_OPERATIONS = 5_000;
28 |
29 | private static final int MAXIMUM_COLLECTION_SIZE = 20;
30 |
31 | private static final int MAXIMUM_INTEGER = 1_000;
32 |
33 | private final long seed;
34 |
35 | private Random randomJavaUtilLinkedList;
36 | private Random randomJavaUtilArrayList;
37 | private Random randomRoddeList;
38 | private Random randomTreeList;
39 |
40 | private IndexedLinkedList roddeList = new IndexedLinkedList<>();
41 | private LinkedList linkedList = new LinkedList<>();
42 | private ArrayList arrayList = new ArrayList<>();
43 | private TreeList treeList = new TreeList<>();
44 |
45 | private long totalMillisRoddeList = 0L;
46 | private long totalMillisLinkedList = 0L;
47 | private long totalMillisArrayList = 0L;
48 | private long totalMillisTreeList = 0L;
49 |
50 | private final boolean runSubListClear;
51 | private final boolean runRemoveAll;
52 | private final boolean runSort;
53 |
54 | private final List[] getLists = new ArrayList[5];
55 |
56 | LinkedListBenchmark(long seed,
57 | boolean runSubListClear,
58 | boolean runRemoveAll,
59 | boolean runSort) {
60 | Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
61 |
62 | this.seed = seed;
63 | this.runSubListClear = runSubListClear;
64 | this.runRemoveAll = runRemoveAll;
65 | this.runSort = runSort;
66 |
67 | for (int i = 0; i < getLists.length; i++) {
68 | getLists[i] = new ArrayList<>(GET_OPERATIONS);
69 | }
70 | }
71 |
72 | void warmup() {
73 | profile(BenchmarkChoice.WARMUP);
74 | }
75 |
76 | void benchmark() {
77 | profile(BenchmarkChoice.BENCHMARK);
78 | }
79 |
80 | private static Integer getRandomInteger(Random random) {
81 | return random.nextInt(MAXIMUM_INTEGER + 1);
82 | }
83 |
84 | private static List createRandomCollection(Random random) {
85 | int size = 1 + random.nextInt(MAXIMUM_COLLECTION_SIZE);
86 |
87 | List list = new ArrayList<>(size);
88 |
89 | for (int i = 0; i < size; i++) {
90 | list.add(getRandomInteger(random));
91 | }
92 |
93 | return list;
94 | }
95 |
96 | private enum BenchmarkChoice { WARMUP, BENCHMARK }
97 |
98 | private void initRandomGenerators() {
99 | randomJavaUtilLinkedList = new Random(seed);
100 | randomJavaUtilArrayList = new Random(seed);
101 | randomRoddeList = new Random(seed);
102 | randomTreeList = new Random(seed);
103 | }
104 |
105 | private void listsEqual() {
106 | listsEqual(roddeList,
107 | linkedList,
108 | arrayList,
109 | treeList);
110 | }
111 |
112 | private static void listsEqual(List... lists) {
113 | if (lists.length < 2) {
114 | throw new IllegalArgumentException("lists.length < 2");
115 | }
116 |
117 | for (int i = 0; i < lists.length - 1; i++) {
118 | if (lists[i].size() != lists[lists.length - 1].size()) {
119 | throw new IllegalStateException("Different size");
120 | }
121 |
122 | Iterator iterator1 = lists[i].iterator();
123 | Iterator iterator2 = lists[lists.length - 1].iterator();
124 |
125 | int elementIndex = 0;
126 |
127 | while (iterator1.hasNext() && iterator2.hasNext()) {
128 | Integer integer1 = iterator1.next();
129 | Integer integer2 = iterator2.next();
130 |
131 | if (!integer1.equals(integer2)) {
132 | throw new IllegalStateException(
133 | "Data mismatch: " + integer1 + " vs. " +
134 | integer2 + " at list " + i +
135 | ", element index: " + elementIndex);
136 | }
137 |
138 | elementIndex++;
139 | }
140 |
141 | if (iterator1.hasNext() || iterator2.hasNext()) {
142 | throw new IllegalStateException("Bad iterators");
143 | }
144 | }
145 | }
146 |
147 | private void profile(BenchmarkChoice benchmarkChoice) {
148 |
149 | printTitle(benchmarkChoice);
150 | initRandomGenerators();
151 |
152 | profileAddFirst();
153 | profileAddLast();
154 | profileAddViaIndex();
155 | profileAppendCollection();
156 | profileAddCollection();
157 | profileGet();
158 | profileRemoveFirst();
159 | profileRemoveLast();
160 | profileRemoveViaIndex();
161 | profileRemoveObject();
162 | profileListIteratorAddition();
163 | profileListIteratorRemoval();
164 | profileStream();
165 | profileParallelStream();
166 |
167 | if (runRemoveAll) {
168 | profileRemoveAll();
169 | }
170 |
171 | if (runSubListClear) {
172 | profileSubListClear();
173 | }
174 |
175 | if (runSort) {
176 | profileSort();
177 | }
178 |
179 | printTotalDurations();
180 |
181 | resetLists();
182 | zeroTimeDurationCounters();
183 |
184 | System.gc();
185 | }
186 |
187 | private void zeroTimeDurationCounters() {
188 | totalMillisArrayList = 0;
189 | totalMillisLinkedList = 0;
190 | totalMillisRoddeList = 0;
191 | totalMillisTreeList = 0;
192 | }
193 |
194 | private void resetLists() {
195 | roddeList = new IndexedLinkedList<>();
196 | linkedList = new java.util.LinkedList<>();
197 | arrayList = new ArrayList<>();
198 | treeList = new TreeList<>();
199 | }
200 |
201 | private void profileAddFirst() {
202 | profileAddFirstRoddeListV2();
203 | profileAddFirstLinkedList();
204 | profileAddFirstArrayList();
205 | profileAddFirstTreeList();
206 |
207 | listsEqual();
208 | System.out.println();
209 | }
210 |
211 | private void profileAddLast() {
212 | profileAddLastRoddeListV2();
213 | profileAddLastLinkedList();
214 | profileAddLastArrayList();
215 | profileAddLastTreeList();
216 |
217 | listsEqual();
218 | System.out.println();
219 | }
220 |
221 | private void profileAddViaIndex() {
222 | profileAddIndexRoddeListV2();
223 | profileAddIndexLinkedList();
224 | profileAddIndexArrayList();
225 | profileAddIndexTreeList();
226 |
227 | listsEqual();
228 | System.out.println();
229 | }
230 |
231 | private void profileAddCollection() {
232 | profileAddCollectionRoddeListV2();
233 | profileAddCollectionLinkedList();
234 | profileAddCollectionArrayList();
235 | profileAddCollectionTreeList();
236 |
237 | listsEqual();
238 | System.out.println();
239 | }
240 |
241 | private void profileGet() {
242 | profileGetRoddeListV2();
243 | profileGetLinkedList();
244 | profileGetArrayList();
245 | profileGetTreeList();
246 |
247 | listsEqual();
248 |
249 | System.out.println();
250 | }
251 |
252 | private void profileRemoveFirst() {
253 | profileRemoveFirstRoddeListV2();
254 | profileRemoveFirstLinkedList();
255 | profileRemoveFirstArrayList();
256 | profileRemoveFirstTreeList();
257 |
258 | listsEqual();
259 | System.out.println();
260 | }
261 |
262 | private void profileRemoveLast() {
263 | profileRemoveLastRoddeList();
264 | profileRemoveLastLinkedList();
265 | profileRemoveLastArrayList();
266 | profileRemoveLastTreeList();
267 |
268 | listsEqual();
269 | System.out.println();
270 | }
271 |
272 | private void profileAppendCollection() {
273 | profileAppendCollectionRoddeListV2();
274 | profileAppendCollectionLinkedList();
275 | profileAppendCollectionArrayList();
276 | profileAppendCollectionTreeList();
277 |
278 | listsEqual();
279 | System.out.println();
280 | }
281 |
282 | private void profileRemoveViaIndex() {
283 | profileRemoveViaIndexRoddeListV2();
284 | profileRemoveViaIndexLinkedList();
285 | profileRemoveViaIndexArrayList();
286 | profileRemoveViaIndexTreeList();
287 |
288 | listsEqual();
289 | System.out.println();
290 | }
291 |
292 | private void profileRemoveObject() {
293 | profileRemoveObjectRoddeListV2();
294 | profileRemoveObjectLinkedList();
295 | profileRemoveObjectArrayList();
296 | profileRemoveObjectTreeList();
297 |
298 | listsEqual();
299 | System.out.println();
300 | }
301 |
302 | private void profileListIteratorAddition() {
303 | profileListIteratorAdditionRoddeListV2();
304 | profileListIteratorAdditionLinkedList();
305 | profileListIteratorAdditionArrayList();
306 | profileListIteratorAdditionTreeList();
307 |
308 | listsEqual();
309 | System.out.println();
310 | }
311 |
312 | private void profileListIteratorRemoval() {
313 | profileListIteratorRemovalRoddeListV2();
314 | profileListIteratorRemovalLinkedList();
315 | profileListIteratorRemovalArrayList();
316 | profileListIteratorRemovalTreeList();
317 |
318 | listsEqual();
319 | System.out.println();
320 | }
321 |
322 | private void profileStream() {
323 | profileStreamRoddeListV2();
324 | profileStreamLinkedList();
325 | profileStreamArrayList();
326 | profileStreamTreeList();
327 |
328 | listsEqual();
329 | System.out.println();
330 | }
331 |
332 | private void profileParallelStream() {
333 | profileParallelStreamRoddeList();
334 | profileParallelStreamLinkedList();
335 | profileParallelStreamArrayList();
336 | profileParallelStreamTreeList();
337 |
338 | Collections.sort(treeList);
339 | Collections.sort(roddeList);
340 | Collections.sort(arrayList);
341 | Collections.sort(linkedList);
342 |
343 | listsEqual();
344 | System.out.println();
345 | }
346 |
347 | private void profileRemoveAll() {
348 | roddeList.clear();
349 | linkedList.clear();
350 | arrayList.clear();
351 | treeList.clear();
352 |
353 | roddeList.addAll(getIntegerArray(10_000));
354 | linkedList.addAll(roddeList);
355 | arrayList.addAll(roddeList);
356 | treeList.addAll(roddeList);
357 |
358 | Collection toRemove = new HashSet<>();
359 | Random random = new Random(seed + 1);
360 |
361 | for (int i = 0; i < 5_000; ++i) {
362 | int value = random.nextInt(5_000);
363 | toRemove.add(value);
364 | }
365 |
366 | profileRemoveAllRoddeList (toRemove);
367 | profileRemoveAllLinkedList (toRemove);
368 | profileRemoveAllArrayList (toRemove);
369 | profileRemoveAllTreeList (toRemove);
370 |
371 | listsEqual();
372 | System.out.println();
373 | }
374 |
375 | private static List getIntegerArray(int size) {
376 | List integers = new ArrayList<>(size);
377 |
378 | for (int i = 0; i < size; ++i) {
379 | integers.add(Integer.valueOf(size % 900_000));
380 | }
381 |
382 | Collections.shuffle(integers);
383 | return integers;
384 | }
385 |
386 | private void profileSubListClear() {
387 | roddeList.clear();
388 | arrayList.clear();
389 | linkedList.clear();
390 | treeList.clear();
391 |
392 | roddeList.addAll(getIntegerArray(1_000_000));
393 | arrayList.addAll(roddeList);
394 | linkedList.addAll(roddeList);
395 | treeList.addAll(roddeList);
396 |
397 | profileSubListClearRoddeList();
398 | profileSubListClearLinkedList();
399 | profileSubListClearArrayList();
400 | profileSubListClearTreeList();
401 |
402 | listsEqual();
403 | System.out.println();
404 | }
405 |
406 | private void profileSort() {
407 | roddeList.clear();
408 | arrayList.clear();
409 | linkedList.clear();
410 | treeList.clear();
411 |
412 | Random random = new Random(seed + 1);
413 |
414 | for (int i = 0; i < 500_000; ++i) {
415 | Integer value = random.nextInt((i % 460_000) + 1);
416 | roddeList.add(value);
417 | arrayList.add(value);
418 | linkedList.add(value);
419 | treeList.add(value);
420 | }
421 |
422 | Collections.shuffle(roddeList, randomRoddeList);
423 | Collections.shuffle(arrayList, randomJavaUtilArrayList);
424 | Collections.shuffle(linkedList, randomJavaUtilLinkedList);
425 | Collections.shuffle(treeList, randomTreeList);
426 |
427 | profileSortRoddeList();
428 | profileSortLinkedList();
429 | profileSortArrayList();
430 | profileSortTreeList();
431 |
432 | listsEqual();
433 | System.out.println();
434 | }
435 |
436 | private void printTotalDurations() {
437 | System.out.println("--- Total time elapsed ---");
438 |
439 | System.out.println(
440 | roddeList.getClass().getName() +
441 | " in (ms): " +
442 | totalMillisRoddeList);
443 |
444 | System.out.println(
445 | linkedList.getClass().getName() +
446 | " in (ms): " +
447 | totalMillisLinkedList);
448 |
449 | System.out.println(
450 | arrayList.getClass().getName() +
451 | " in (ms): " +
452 | totalMillisArrayList);
453 |
454 | System.out.println(
455 | treeList.getClass().getName() +
456 | " in (ms): " +
457 | totalMillisTreeList);
458 | }
459 |
460 | private long profileAddFirst(
461 | List list,
462 | int operations,
463 | Random random) {
464 |
465 | long startMillis = System.currentTimeMillis();
466 |
467 | for (int i = 0; i < operations; i++) {
468 | list.add(0, getRandomInteger(random));
469 | }
470 |
471 | long endMillis = System.currentTimeMillis();
472 | long durationMillis = endMillis - startMillis;
473 |
474 | System.out.println(
475 | list.getClass().getName() +
476 | ".addFirst in (ms): " +
477 | durationMillis);
478 |
479 | return durationMillis;
480 | }
481 |
482 | private long profileAddLast(
483 | List list,
484 | int operations,
485 | Random random) {
486 |
487 | long startMillis = System.currentTimeMillis();
488 |
489 | for (int i = 0; i < operations; i++) {
490 | list.add(getRandomInteger(random));
491 | }
492 |
493 | long endMillis = System.currentTimeMillis();
494 | long durationMillis = endMillis - startMillis;
495 |
496 | System.out.println(
497 | list.getClass().getName() +
498 | ".addLast in (ms): " +
499 | durationMillis);
500 |
501 | return durationMillis;
502 | }
503 |
504 | private long profileAddIndex(
505 | List list,
506 | int operations,
507 | Random random) {
508 |
509 | long startMillis = System.currentTimeMillis();
510 |
511 | for (int i = 0; i < operations; i++) {
512 | int index = random.nextInt(list.size());
513 | Integer value = getRandomInteger(random);
514 | list.add(index, value);
515 | }
516 |
517 | long endMillis = System.currentTimeMillis();
518 | long durationMillis = endMillis - startMillis;
519 |
520 | System.out.println(
521 | list.getClass().getName() +
522 | ".add(int, E) in (ms): " +
523 | durationMillis);
524 |
525 | return durationMillis;
526 | }
527 |
528 | private long profileAddCollection(
529 | List list,
530 | int operations,
531 | Random random) {
532 |
533 | long startMillis = System.currentTimeMillis();
534 |
535 | for (int i = 0; i < operations; i++) {
536 | List collection = createRandomCollection(random);
537 | int index = random.nextInt(list.size());
538 | list.addAll(index, collection);
539 | }
540 |
541 | long endMillis = System.currentTimeMillis();
542 | long durationMillis = endMillis - startMillis;
543 |
544 | System.out.println(
545 | list.getClass().getName() +
546 | ".addAll(int, Collection) in (ms): " +
547 | durationMillis);
548 |
549 | return durationMillis;
550 | }
551 |
552 | private long profileAppendCollection(
553 | List list,
554 | int operations,
555 | Random random) {
556 |
557 | long startMillis = System.currentTimeMillis();
558 |
559 | for (int i = 0; i < operations; i++) {
560 | List collection = createRandomCollection(random);
561 | list.addAll(collection);
562 | }
563 |
564 | long endMillis = System.currentTimeMillis();
565 | long durationMillis = endMillis - startMillis;
566 |
567 | System.out.println(
568 | list.getClass().getName() +
569 | ".addAll(Collection) in (ms): " +
570 | durationMillis);
571 |
572 | return durationMillis;
573 | }
574 |
575 | private long profileGet(List list, int operations, Random random) {
576 | long startMillis = System.currentTimeMillis();
577 |
578 | for (int i = 0; i < operations; i++) {
579 | list.get(random.nextInt(list.size()));
580 | }
581 |
582 | long endMillis = System.currentTimeMillis();
583 | long durationMillis = endMillis - startMillis;
584 |
585 | System.out.println(
586 | list.getClass().getName() + ".get(int) in (ms): " +
587 | durationMillis);
588 |
589 | return durationMillis;
590 | }
591 |
592 | private long profileRemoveFirst(List list) {
593 | long startMillis = System.currentTimeMillis();
594 |
595 | if (!list.getClass().equals(Deque.class)) {
596 | for (int i = 0; i < REMOVE_FIRST_OPERATIONS; i++) {
597 | list.remove(0);
598 | }
599 | } else {
600 | for (int i = 0; i < REMOVE_FIRST_OPERATIONS; i++) {
601 | ((Deque) list).removeFirst();
602 | }
603 | }
604 |
605 |
606 | long endMillis = System.currentTimeMillis();
607 | long durationMillis = endMillis - startMillis;
608 |
609 | System.out.println(
610 | list.getClass().getName() + ".removeFirst() in (ms): " +
611 | durationMillis);
612 |
613 | return durationMillis;
614 | }
615 |
616 | private long profileRemoveLast(List list) {
617 | long startMillis;
618 | long endMillis;
619 |
620 | if (list instanceof Deque) {
621 | startMillis = System.currentTimeMillis();
622 |
623 | for (int i = 0; i < REMOVE_FIRST_OPERATIONS; ++i) {
624 | ((Deque) list).removeLast();
625 | }
626 |
627 | endMillis = System.currentTimeMillis();
628 | } else {
629 | startMillis = System.currentTimeMillis();
630 |
631 | for (int i = 0; i < REMOVE_FIRST_OPERATIONS; ++i) {
632 | list.remove(list.size() - 1);
633 | }
634 |
635 | endMillis = System.currentTimeMillis();
636 | }
637 |
638 | long durationMillis = endMillis - startMillis;
639 |
640 | System.out.println(
641 | list.getClass().getName() + ".removeLast() in (ms): " +
642 | durationMillis);
643 |
644 | return durationMillis;
645 | }
646 |
647 | private long profileRemoveViaIndex(
648 | List list,
649 | int operations,
650 | Random random) {
651 |
652 | long startMillis = System.currentTimeMillis();
653 |
654 | for (int i = 0; i < operations; i++) {
655 | list.remove(random.nextInt(list.size()));
656 | }
657 |
658 | long endMillis = System.currentTimeMillis();
659 | long durationMillis = endMillis - startMillis;
660 |
661 | System.out.println(
662 | list.getClass().getName() +
663 | ".remove(int) in (ms): " +
664 | durationMillis);
665 |
666 | return durationMillis;
667 | }
668 |
669 | private long profileRemoveObject(
670 | List list,
671 | int operations,
672 | Random random) {
673 |
674 | long startMillis = System.currentTimeMillis();
675 |
676 | for (int i = 0; i < operations; i++) {
677 | list.remove(Integer.valueOf(getRandomInteger(random)));
678 | }
679 |
680 | long endMillis = System.currentTimeMillis();
681 | long durationMillis = endMillis - startMillis;
682 |
683 | System.out.println(
684 | list.getClass().getName() +
685 | ".remove(Object) in (ms): " +
686 | durationMillis);
687 |
688 | return durationMillis;
689 | }
690 |
691 | private long profileListIteratorRemoval(List list) {
692 | long startMillis = System.currentTimeMillis();
693 | Iterator iterator = list.iterator();
694 | int counter = 0;
695 |
696 | while (iterator.hasNext()) {
697 | iterator.next();
698 |
699 | // Remove every 2nd element:
700 | if (counter % 10 == 0) {
701 | try {
702 | iterator.remove();
703 | } catch (AssertionError ae) {
704 | System.err.println(ae.getMessage());
705 | System.exit(1);
706 | }
707 | }
708 |
709 | counter++;
710 | }
711 |
712 | long endMillis = System.currentTimeMillis();
713 | long durationMillis = endMillis - startMillis;
714 |
715 | System.out.println(
716 | list.getClass().getName() +
717 | ".iterator().remove() in (ms): " +
718 | durationMillis);
719 |
720 | return durationMillis;
721 | }
722 |
723 | private long profileListIteratorAddition(
724 | List list, Random random) {
725 |
726 | long startMillis = System.currentTimeMillis();
727 | ListIterator iterator = list.listIterator(1);
728 | int counter = 0;
729 |
730 | while (iterator.hasNext()) {
731 | iterator.next();
732 |
733 | // Remove every 2nd element:
734 | if (counter % 10 == 0) {
735 | try {
736 | Integer integer = Integer.valueOf(random.nextInt(10_000));
737 | iterator.add(integer);
738 | } catch (AssertionError ae) {
739 | System.err.println(ae.getMessage());
740 | System.exit(1);
741 | }
742 | }
743 |
744 | counter++;
745 | }
746 |
747 | long endMillis = System.currentTimeMillis();
748 | long durationMillis = endMillis - startMillis;
749 |
750 | System.out.println(
751 | list.getClass().getName() +
752 | ".iterator().add() in (ms): " +
753 | durationMillis);
754 |
755 | return durationMillis;
756 | }
757 |
758 | private long profileStream(List list) {
759 | long startMillis = System.currentTimeMillis();
760 |
761 | List newList =
762 | list.stream().map(x -> 2 * x).collect(Collectors.toList());
763 |
764 | long endMillis = System.currentTimeMillis();
765 | long durationMillis = endMillis - startMillis;
766 |
767 | list.clear();
768 | list.addAll(newList);
769 |
770 | System.out.println(
771 | list.getClass().getName() +
772 | ".stream() in (ms): " + durationMillis);
773 |
774 | return durationMillis;
775 | }
776 |
777 | private long profileParallelStream(List list) {
778 | long startMillis = System.currentTimeMillis();
779 |
780 | List newList = list.stream()
781 | .parallel()
782 | .map(x -> 2 * x)
783 | .collect(Collectors.toList());
784 |
785 | long endMillis = System.currentTimeMillis();
786 | long durationMillis = endMillis - startMillis;
787 |
788 | list.clear();
789 | list.addAll(newList);
790 |
791 | System.out.println(
792 | list.getClass().getName() +
793 | ".stream().parallel() in (ms): " + durationMillis);
794 |
795 | return durationMillis;
796 | }
797 |
798 | private long profileRemoveAll(List list,
799 | Collection toRemove) {
800 | long startMillis;
801 | long endMillis;
802 |
803 | if (list instanceof TreeList || list instanceof LinkedList) {
804 | startMillis = System.currentTimeMillis();
805 |
806 | for (Integer i : toRemove) {
807 | list.remove(i);
808 | }
809 |
810 | endMillis = System.currentTimeMillis();
811 | } else {
812 | startMillis = System.currentTimeMillis();
813 | list.removeAll(toRemove);
814 | endMillis = System.currentTimeMillis();
815 | }
816 |
817 | long durationMillis = endMillis - startMillis;
818 |
819 | System.out.println(
820 | list.getClass().getName() +
821 | ".removeAll() in (ms): " + durationMillis);
822 |
823 | return durationMillis;
824 | }
825 |
826 | private long profileSubListClear(List list) {
827 | int fromIndex = list.size() / 2;
828 | int toIndex = list.size() / 2 + 1;
829 |
830 | long startMillis = System.currentTimeMillis();
831 |
832 | // Clear short range:
833 | list.subList(fromIndex, toIndex).clear();
834 |
835 | fromIndex = 10;
836 | toIndex = list.size() - 9;
837 |
838 | // Clear long range:
839 | list.subList(fromIndex, toIndex).clear();
840 |
841 | long endMillis = System.currentTimeMillis();
842 |
843 | long durationMillis = endMillis - startMillis;
844 |
845 | System.out.println(
846 | list.getClass().getName() +
847 | ".subList(...).clear() in (ms): " +
848 | durationMillis);
849 |
850 | return durationMillis;
851 | }
852 |
853 | private long profileSort(List list) {
854 | long startMillis = System.currentTimeMillis();
855 |
856 | list.subList(10, list.size() - 10).sort(Integer::compare);
857 |
858 | long endMillis = System.currentTimeMillis();
859 | long durationMillis = endMillis - startMillis;
860 |
861 | System.out.println(
862 | list.getClass()
863 | .getSimpleName()
864 | + ".subList().sort() in (ms): "
865 | + durationMillis);
866 |
867 | return durationMillis;
868 | }
869 |
870 | private void profileAddFirstRoddeListV2() {
871 | totalMillisRoddeList +=
872 | profileAddFirst(
873 | roddeList,
874 | ADD_FIRST_OPERATIONS,
875 | randomRoddeList);
876 | }
877 |
878 | private void profileAddFirstLinkedList() {
879 | totalMillisLinkedList +=
880 | profileAddFirst(linkedList,
881 | ADD_FIRST_OPERATIONS,
882 | randomJavaUtilLinkedList);
883 | }
884 |
885 | private void profileAddFirstArrayList() {
886 | totalMillisArrayList +=
887 | profileAddFirst(arrayList,
888 | ADD_FIRST_OPERATIONS,
889 | randomJavaUtilArrayList);
890 | }
891 |
892 | private void profileAddFirstTreeList() {
893 | totalMillisTreeList +=
894 | profileAddFirst(treeList,
895 | ADD_FIRST_OPERATIONS,
896 | randomTreeList);
897 | }
898 |
899 | private void profileAddLastRoddeListV2() {
900 | totalMillisRoddeList +=
901 | profileAddLast(roddeList, ADD_LAST_OPERATIONS, randomRoddeList);
902 | }
903 |
904 | private void profileAddLastLinkedList() {
905 | totalMillisLinkedList +=
906 | profileAddLast(
907 | linkedList,
908 | ADD_LAST_OPERATIONS,
909 | randomJavaUtilLinkedList);
910 | }
911 |
912 | private void profileAddLastArrayList() {
913 | totalMillisArrayList +=
914 | profileAddLast(arrayList,
915 | ADD_LAST_OPERATIONS,
916 | randomJavaUtilArrayList);
917 | }
918 |
919 | private void profileAddLastTreeList() {
920 | totalMillisTreeList +=
921 | profileAddLast(treeList,
922 | ADD_LAST_OPERATIONS,
923 | randomTreeList);
924 | }
925 |
926 | private void profileAddIndexRoddeListV2() {
927 | totalMillisRoddeList +=
928 | profileAddIndex(roddeList,
929 | ADD_AT_OPERATIONS,
930 | randomRoddeList);
931 | }
932 |
933 | private void profileAddIndexLinkedList() {
934 | totalMillisLinkedList +=
935 | profileAddIndex(
936 | linkedList,
937 | ADD_AT_OPERATIONS,
938 | randomJavaUtilLinkedList);
939 | }
940 |
941 | private void profileAddIndexArrayList() {
942 | totalMillisArrayList +=
943 | profileAddIndex(
944 | arrayList,
945 | ADD_AT_OPERATIONS,
946 | randomJavaUtilArrayList);
947 | }
948 |
949 | private void profileAddIndexTreeList() {
950 | totalMillisTreeList +=
951 | profileAddIndex(
952 | treeList,
953 | ADD_AT_OPERATIONS,
954 | randomTreeList);
955 | }
956 |
957 | private void profileAddCollectionRoddeListV2() {
958 | totalMillisRoddeList +=
959 | profileAddCollection(
960 | roddeList,
961 | ADD_COLLECTION_AT_OPERATIONS,
962 | randomRoddeList);
963 | }
964 |
965 | private void profileAddCollectionLinkedList() {
966 | totalMillisLinkedList +=
967 | profileAddCollection(
968 | linkedList,
969 | ADD_COLLECTION_AT_OPERATIONS,
970 | randomJavaUtilLinkedList);
971 | }
972 |
973 | private void profileAddCollectionArrayList() {
974 | totalMillisArrayList +=
975 | profileAddCollection(
976 | arrayList,
977 | ADD_COLLECTION_AT_OPERATIONS,
978 | randomJavaUtilArrayList);
979 | }
980 |
981 | private void profileAddCollectionTreeList() {
982 | totalMillisTreeList +=
983 | profileAddCollection(
984 | treeList,
985 | ADD_COLLECTION_AT_OPERATIONS,
986 | randomTreeList);
987 | }
988 |
989 | private void profileGetRoddeListV2() {
990 | totalMillisRoddeList +=
991 | profileGet(roddeList, GET_OPERATIONS, randomRoddeList);
992 | }
993 |
994 | private void profileGetLinkedList() {
995 | totalMillisLinkedList +=
996 | profileGet(linkedList,
997 | GET_OPERATIONS,
998 | randomJavaUtilLinkedList);
999 | }
1000 |
1001 | private void profileGetArrayList() {
1002 | totalMillisArrayList +=
1003 | profileGet(arrayList,
1004 | GET_OPERATIONS,
1005 | randomJavaUtilArrayList);
1006 | }
1007 |
1008 | private void profileGetTreeList() {
1009 | totalMillisTreeList +=
1010 | profileGet(treeList, GET_OPERATIONS, randomTreeList);
1011 | }
1012 |
1013 | private void profileRemoveFirstRoddeListV2() {
1014 | totalMillisRoddeList += profileRemoveFirst(roddeList);
1015 | }
1016 |
1017 | private void profileRemoveFirstLinkedList() {
1018 | totalMillisLinkedList += profileRemoveFirst(linkedList);
1019 | }
1020 |
1021 | private void profileRemoveFirstArrayList() {
1022 | totalMillisArrayList += profileRemoveFirst(arrayList);
1023 | }
1024 |
1025 | private void profileRemoveFirstTreeList() {
1026 | totalMillisTreeList += profileRemoveFirst(treeList);
1027 | }
1028 |
1029 | private void profileRemoveLastRoddeList() {
1030 | totalMillisRoddeList += profileRemoveLast(roddeList);
1031 | }
1032 |
1033 | private void profileRemoveLastLinkedList() {
1034 | totalMillisLinkedList += profileRemoveLast(linkedList);
1035 | }
1036 |
1037 | private void profileRemoveLastArrayList() {
1038 | totalMillisArrayList += profileRemoveLast(arrayList);
1039 | }
1040 |
1041 | private void profileRemoveLastTreeList() {
1042 | totalMillisTreeList += profileRemoveLast(treeList);
1043 | }
1044 |
1045 | private void profileAppendCollectionRoddeListV2() {
1046 | totalMillisRoddeList +=
1047 | profileAppendCollection(
1048 | roddeList,
1049 | ADD_LAST_COLLECTION_OPERATIONS,
1050 | randomRoddeList);
1051 | }
1052 |
1053 | private void profileAppendCollectionLinkedList() {
1054 | totalMillisLinkedList +=
1055 | profileAppendCollection(
1056 | linkedList,
1057 | ADD_LAST_COLLECTION_OPERATIONS,
1058 | randomJavaUtilLinkedList);
1059 | }
1060 |
1061 | private void profileAppendCollectionArrayList() {
1062 | totalMillisArrayList +=
1063 | profileAppendCollection(
1064 | arrayList,
1065 | ADD_LAST_COLLECTION_OPERATIONS,
1066 | randomJavaUtilArrayList);
1067 | }
1068 |
1069 | private void profileAppendCollectionTreeList() {
1070 | totalMillisTreeList +=
1071 | profileAppendCollection(
1072 | treeList,
1073 | ADD_LAST_COLLECTION_OPERATIONS,
1074 | randomTreeList);
1075 | }
1076 |
1077 | private void profileRemoveViaIndexRoddeListV2() {
1078 | totalMillisRoddeList +=
1079 | profileRemoveViaIndex(
1080 | roddeList,
1081 | REMOVE_VIA_INDEX_OPERATIONS,
1082 | randomRoddeList);
1083 | }
1084 |
1085 | private void profileRemoveViaIndexLinkedList() {
1086 | totalMillisLinkedList +=
1087 | profileRemoveViaIndex(
1088 | linkedList,
1089 | REMOVE_VIA_INDEX_OPERATIONS,
1090 | randomJavaUtilLinkedList);
1091 | }
1092 |
1093 | private void profileRemoveViaIndexArrayList() {
1094 | totalMillisArrayList +=
1095 | profileRemoveViaIndex(
1096 | arrayList,
1097 | REMOVE_VIA_INDEX_OPERATIONS,
1098 | randomJavaUtilArrayList);
1099 | }
1100 |
1101 | private void profileRemoveViaIndexTreeList() {
1102 | totalMillisTreeList +=
1103 | profileRemoveViaIndex(
1104 | treeList,
1105 | REMOVE_VIA_INDEX_OPERATIONS,
1106 | randomTreeList);
1107 | }
1108 |
1109 | private void profileRemoveObjectRoddeListV2() {
1110 | totalMillisRoddeList +=
1111 | profileRemoveObject(
1112 | roddeList,
1113 | REMOVE_OBJECT_OPERATIONS,
1114 | randomRoddeList);
1115 | }
1116 |
1117 | private void profileRemoveObjectLinkedList() {
1118 | totalMillisLinkedList +=
1119 | profileRemoveObject(
1120 | linkedList,
1121 | REMOVE_OBJECT_OPERATIONS,
1122 | randomJavaUtilLinkedList);
1123 | }
1124 |
1125 | private void profileRemoveObjectArrayList() {
1126 | totalMillisArrayList +=
1127 | profileRemoveObject(
1128 | arrayList,
1129 | REMOVE_OBJECT_OPERATIONS,
1130 | randomJavaUtilArrayList);
1131 | }
1132 |
1133 | private void profileRemoveObjectTreeList() {
1134 | totalMillisTreeList +=
1135 | profileRemoveObject(
1136 | treeList,
1137 | REMOVE_OBJECT_OPERATIONS,
1138 | randomTreeList);
1139 | }
1140 |
1141 | private void profileListIteratorRemovalRoddeListV2() {
1142 | totalMillisRoddeList += profileListIteratorRemoval(roddeList);
1143 | }
1144 |
1145 | private void profileListIteratorRemovalLinkedList() {
1146 | totalMillisLinkedList += profileListIteratorRemoval(linkedList);
1147 | }
1148 |
1149 | private void profileListIteratorRemovalArrayList() {
1150 | totalMillisArrayList += profileListIteratorRemoval(arrayList);
1151 | }
1152 |
1153 | private void profileListIteratorRemovalTreeList() {
1154 | totalMillisTreeList += profileListIteratorRemoval(treeList);
1155 | }
1156 |
1157 | private void profileListIteratorAdditionRoddeListV2() {
1158 | totalMillisRoddeList +=
1159 | profileListIteratorAddition(roddeList, randomRoddeList);
1160 | }
1161 |
1162 | private void profileListIteratorAdditionLinkedList() {
1163 | totalMillisLinkedList +=
1164 | profileListIteratorAddition(
1165 | linkedList,
1166 | randomJavaUtilLinkedList);
1167 | }
1168 |
1169 | private void profileListIteratorAdditionArrayList() {
1170 | totalMillisArrayList +=
1171 | profileListIteratorAddition(
1172 | arrayList,
1173 | randomJavaUtilArrayList);
1174 | }
1175 |
1176 | private void profileListIteratorAdditionTreeList() {
1177 | totalMillisTreeList +=
1178 | profileListIteratorAddition(treeList, randomTreeList);
1179 | }
1180 |
1181 | private void profileStreamRoddeListV2() {
1182 | totalMillisRoddeList += profileStream(roddeList);
1183 | }
1184 |
1185 | private void profileStreamLinkedList() {
1186 | totalMillisLinkedList += profileStream(linkedList);
1187 | }
1188 |
1189 | private void profileStreamArrayList() {
1190 | totalMillisArrayList += profileStream(arrayList);
1191 | }
1192 |
1193 | private void profileStreamTreeList() {
1194 | totalMillisTreeList += profileStream(treeList);
1195 | }
1196 |
1197 | private void profileParallelStreamRoddeList() {
1198 | totalMillisRoddeList += profileParallelStream(roddeList);
1199 | }
1200 |
1201 | private void profileParallelStreamLinkedList() {
1202 | totalMillisLinkedList += profileParallelStream(linkedList);
1203 | }
1204 |
1205 | private void profileParallelStreamArrayList() {
1206 | totalMillisArrayList += profileParallelStream(arrayList);
1207 | }
1208 |
1209 | private void profileParallelStreamTreeList() {
1210 | totalMillisTreeList += profileParallelStream(treeList);
1211 | }
1212 |
1213 | private void profileRemoveAllRoddeList(Collection toRemove) {
1214 | totalMillisRoddeList += profileRemoveAll(roddeList, toRemove);
1215 | }
1216 |
1217 | private void profileRemoveAllLinkedList(Collection toRemove) {
1218 | totalMillisLinkedList += profileRemoveAll(linkedList, toRemove);
1219 | }
1220 |
1221 | private void profileRemoveAllArrayList(Collection toRemove) {
1222 | totalMillisArrayList += profileRemoveAll(arrayList, toRemove);
1223 | }
1224 |
1225 | private void profileRemoveAllTreeList(Collection toRemove) {
1226 | totalMillisTreeList += profileRemoveAll(treeList, toRemove);
1227 | }
1228 |
1229 | private void profileSubListClearRoddeList() {
1230 | totalMillisRoddeList += profileSubListClear(roddeList);
1231 | }
1232 |
1233 | private void profileSubListClearLinkedList() {
1234 | totalMillisLinkedList += profileSubListClear(linkedList);
1235 | }
1236 |
1237 | private void profileSubListClearArrayList() {
1238 | totalMillisArrayList += profileSubListClear(arrayList);
1239 | }
1240 |
1241 | private void profileSubListClearTreeList() {
1242 | totalMillisTreeList += profileSubListClear(treeList);
1243 | }
1244 |
1245 | private void profileSortRoddeList() {
1246 | totalMillisRoddeList += profileSort(roddeList);
1247 | }
1248 |
1249 | private void profileSortLinkedList() {
1250 | totalMillisLinkedList += profileSort(linkedList);
1251 | }
1252 |
1253 | private void profileSortArrayList() {
1254 | totalMillisArrayList += profileSort(arrayList);
1255 | }
1256 |
1257 | private void profileSortTreeList() {
1258 | totalMillisTreeList += profileSort(treeList);
1259 | }
1260 |
1261 | private void printTitle(BenchmarkChoice benchmarkChoice) {
1262 | switch (benchmarkChoice) {
1263 | case WARMUP:
1264 | System.out.println("=== WARMUP RUN ===");
1265 | break;
1266 |
1267 | case BENCHMARK:
1268 | System.out.println("=== BENCHMARK RUN ===");
1269 | break;
1270 | }
1271 | }
1272 |
1273 | private void clearGetLists() {
1274 | for (List list : getLists) {
1275 | list.clear();
1276 | }
1277 | }
1278 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/coderodde/util/benchmark/LinkedListBenchmark2.java:
--------------------------------------------------------------------------------
1 | package com.github.coderodde.util.benchmark;
2 |
3 | import com.github.coderodde.util.IndexedLinkedList;
4 | import java.util.ArrayList;
5 | import java.util.Deque;
6 | import java.util.HashMap;
7 | import java.util.LinkedHashMap;
8 | import java.util.LinkedList;
9 | import java.util.List;
10 | import java.util.Map;
11 | import java.util.Random;
12 | import java.util.TreeMap;
13 | import net.openhft.affinity.AffinityLock;
14 | import org.apache.commons.collections4.list.TreeList;
15 |
16 | public class LinkedListBenchmark2 {
17 |
18 | private static final Object ELEMENT = new Object();
19 | private static final Map DURATION_COUNTER_MAP =
20 | new HashMap<>();
21 |
22 | private static final Map PER_OPERATION_DURATION_COUNTER_MAP =
23 | new TreeMap<>();
24 |
25 | private static final Map
26 | BENCHMARK_OPERATION_NAME_TO_GNUPLOT_NAME = new LinkedHashMap<>();
27 |
28 | private static final Map COLOR_MAP = new HashMap<>();
29 |
30 | private static final int[] LIST_SIZES = {
31 | 100_000,
32 | 200_000,
33 | 300_000,
34 | 400_000,
35 | 500_000,
36 | 600_000,
37 | 700_000,
38 | 800_000,
39 | 900_000,
40 | 1_000_000,
41 | };
42 |
43 | private static final class Bounds {
44 | static final int NUMBER_OF_ADDITIONS_AT_BEGINNING = 2_000;
45 | static final int NUMBER_OF_RANDOM_ADDS = 2_000;
46 | static final int NUMBER_OF_ADDITIONS_AT_END = 10_000;
47 | static final int NUMBER_OF_GETS = 500;
48 | static final int NUMBER_OF_REMOVE_FIRST_OPS = 5_000;
49 | static final int NUMBER_OF_REMOVE_LAST_OPS = 20_000;
50 | static final int NUMBER_OF_RANDOM_REMOVES = 10_000;
51 | static final int NUMBER_OF_COLLECTION_APPENDS = 50;
52 | static final int NUMBER_OF_COLLECTION_INSERTS = 50;
53 | static final int APPEND_COLLECTION_SIZE = 10_000;
54 | static final int INSERT_COLLECTION_SIZE = 3_500;
55 | static final int REMOVE_RANGE_SIZE = 500;
56 | }
57 |
58 | private static final String[] METHOD_NAMES = {
59 | "AddAtBeginning",
60 | "AddAtEnd",
61 | "AddRandom",
62 | "PrependCollection",
63 | "AppendCollection",
64 | "InsertCollection",
65 | "GetRandom",
66 | "RemoveFromBeginning",
67 | "RemoveFromEnd",
68 | "RemoveRandom",
69 | "RemoveRange",
70 | };
71 |
72 | private static final String[] LIST_TYPE_NAMES = {
73 | "arrayList",
74 | "indexedLinkedList",
75 | "linkedList",
76 | "treeList",
77 | };
78 |
79 | static {
80 | clearDurationCounterMap();
81 | initializeOperationNames();
82 | initializePerOperationMap();
83 | initializeColorMap();
84 | }
85 |
86 | private static void clearDurationCounterMap() {
87 | for (int i = 0; i < LIST_TYPE_NAMES.length; i++) {
88 | DURATION_COUNTER_MAP.put(LIST_TYPE_NAMES[i], 0L);;
89 | }
90 | }
91 |
92 | private static void clearPerOperationCounterMap() {
93 | for (final String listTypeName : LIST_TYPE_NAMES) {
94 | for (final String methodName : METHOD_NAMES) {
95 | final String line =
96 | String.format(
97 | "%s%s",
98 | listTypeName,
99 | methodName);
100 |
101 | PER_OPERATION_DURATION_COUNTER_MAP.put(line, 0L);
102 | }
103 | }
104 | }
105 |
106 | private static void initializeOperationNames() {
107 | Map m = BENCHMARK_OPERATION_NAME_TO_GNUPLOT_NAME;
108 |
109 | m.put("AddAtBeginning", "Push-Front");
110 | m.put("AddAtEnd", "Push-Back");
111 | m.put("AddRandom", "Insert");
112 | m.put("PrependCollection", "Push-Front-Collection");
113 | m.put("AppendCollection", "Push-Back-Collection");
114 | m.put("InsertCollection", "Insert-Collection");
115 | m.put("GetRandom", "Search");
116 | m.put("RemoveFromBeginning", "Pop-Front");
117 | m.put("RemoveFromEnd", "Pop-Back");
118 | m.put("RemoveRandom", "Delete");
119 | m.put("RemoveRange", "Delete-Range");
120 | }
121 |
122 | private static void initializeColorMap() {
123 | final String insertColor = "0x289e37";
124 | final String searchColor = "0x28579e";
125 | final String deleteColor = "0xa83232";
126 |
127 | COLOR_MAP.put("Push-Front", insertColor);
128 | COLOR_MAP.put("Push-Back", insertColor);
129 | COLOR_MAP.put("Insert", insertColor);
130 | COLOR_MAP.put("Push-Front-Collection", insertColor);
131 | COLOR_MAP.put("Push-Back-Collection", insertColor);
132 | COLOR_MAP.put("Insert-Collection", insertColor);
133 |
134 | COLOR_MAP.put("Search", searchColor);
135 |
136 | COLOR_MAP.put("Pop-Front", deleteColor);
137 | COLOR_MAP.put("Pop-Back", deleteColor);
138 | COLOR_MAP.put("Delete", deleteColor);
139 | COLOR_MAP.put("Delete-Range", deleteColor);
140 | }
141 |
142 | private static void initializePerOperationMap() {
143 | // Initialize the per operation duration map:
144 | for (String listName : LIST_TYPE_NAMES) {
145 | for (String methodName : METHOD_NAMES) {
146 | PER_OPERATION_DURATION_COUNTER_MAP
147 | .put(String.format(
148 | "%s%s",
149 | listName,
150 | methodName),
151 | 0L);
152 | }
153 | }
154 | }
155 |
156 | public static void main(String[] args) {
157 | // deleteRange();
158 | // System.exit(0);
159 |
160 | try (AffinityLock al = AffinityLock.acquireLock()) {
161 | Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
162 | warmupRemove();
163 | benchmarkRemove();
164 | System.exit(0);
165 | clearDurationCounterMap();
166 | warmup();
167 | benchmark();
168 | System.out.println("<<< Total durations >>>");
169 | printTotalDurations();
170 | }
171 | }
172 |
173 | private static void deleteRange() {
174 | System.out.println("deleteRange():");
175 | final List