You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
description = "A linked list implementation with unique features and an extended list of constant time methods providing high performance traversals and mutations."
Copy file name to clipboardExpand all lines: README.md
+29-31Lines changed: 29 additions & 31 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,7 +7,8 @@ A linked list implementation with unique features and an extended list of consta
7
7
8
8
Both doubly and singly lists are provided as generic variants of the core struct `List`. It is sufficient to know the four variants:
9
9
*[`DoublyList`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyList.html) and [`SinglyList`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.SinglyList.html)
10
-
*[`DoublyListLazy`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyListLazy.html) and [`SinglyListLazy`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.SinglyListLazy.html) -> lazy suffix corresponds to lazy memory reclaim and will be explained in the indices section.
10
+
*[`DoublyListLazy`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyListLazy.html) and [`SinglyListLazy`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.SinglyListLazy.html)
11
+
**Lazy* suffix corresponds to lazy memory reclaim and will be explained in the indices section.
11
12
12
13
Some notable features are as follows.
13
14
@@ -18,15 +19,15 @@ Link lists are self organizing to keep the nodes close to each other to benefit
18
19
We observe in benchmarks that `DoublyList` is significantly faster than the standard linked list.
19
20
20
21
<details>
21
-
<summarystyle="font-weight:bold;">A. Mutation At Ends</summary>
22
+
<summarystyle="font-weight:bold;">A. Benchmark & Example: Mutation At Ends</summary>
22
23
23
24
In [doubly_mutation_ends.rs](https://github.com/orxfun/orx-linked-list/blob/main/benches/doubly_mutation_ends.rs) benchmark, we first push elements to a linked list until it reaches a particular length. Then, we call
24
25
-`push_back`
25
26
-`push_front`
26
27
-`pop_back`
27
28
-`pop_front`
28
29
29
-
in a random alternating order. We observe that `DoublyList` is around **40%** faster than `std::collections::LinkedList`.
30
+
in a random alternating order. We observe that **DoublyList** is around **40% faster than std::collections::LinkedList**.
30
31
31
32
The following example demonstrates the simple usage of the list with mutation at its ends.
<summarystyle="font-weight:bold;">B. Benchmark & Example: Iteration or Sequential Access</summary>
56
57
57
58
In [doubly_iter.rs](https://github.com/orxfun/orx-linked-list/blob/main/benches/doubly_iter.rs) benchmark, we create a linked list with insertions to the back and front of the list in a random order. Then, we `iter()` over the nodes and apply a map-reduce over the elements.
58
59
59
-
We observe that `DoublyList` iteration is around **25 times** faster than that with `std::collections::LinkedList`.
60
+
We observe that **DoublyList** iteration is around **25 times faster than that with std::collections::LinkedList**.
60
61
61
62
The significance of improvement can further be increased by using `DoublyList::iter_x()` instead, which iterates over the elements in an arbitrary order. Unordered iteration is not suitable for all use cases. Most reductions or applying a mutation to each element are a couple of common examples. When the use case allows, unordered iteration further provides significant speed up.
62
63
63
64
</details>
64
65
65
66
## Iterations
66
67
67
-
Linked lists are all about traversal. And hence, the linked list, specifically the `DoublyList`, provides various useful ways to iterate over the data:
68
-
*[`iter()`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter) iterates from front to back of the list
69
-
*`iter().rev()` iterates from back to front
70
-
*[`iter_from(idx: &DoublyIdx<T>)`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter_from) iterates forward starting from the node with the given index to the back
71
-
*[`iter_backward_from(idx: &DoublyIdx<T>)`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter_backward_from) iterates forward starting from the node with the given index to the front
72
-
*[`ring_iter(pivot_idx: &DoublyIdx<T>)`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.ring_iter) iterates forward starting from the pivot node with the given index until the node before the pivot node, linking back to the front and giving the list the **circular behavior**
73
-
*[`iter_links()`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter_links) iterates over the links of the list
74
-
*[`iter_x()`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyList.html#method.iter_x) iterates over elements in an arbitrary order, which is often faster when the order is not required
68
+
Linked lists are all about traversal. Therefore, the linked lists defined in this crate, especially the **DoublyList**, provide various useful ways to iterate over the data:
69
+
70
+
|||
71
+
|------------|------------|
72
+
|[`iter()`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter)| from front to back of the list |
73
+
|`iter().rev()`| from back to front |
74
+
|[`iter_from(idx: &DoublyIdx<T>)`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter_from)| forward starting from the node with the given index to the back |
75
+
|[`iter_backward_from(idx: &DoublyIdx<T>)`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter_backward_from)| backward starting from the node with the given index to the front |
76
+
|[`ring_iter(pivot_idx: &DoublyIdx<T>)`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.ring_iter)| forward starting from the pivot node with the given index until the node before the pivot node, linking back to the front and giving the list the **circular behavior**|
77
+
|[`iter_links()`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter_links)| forward over the links, rather than nodes, from front to back |
78
+
|[`iter_x()`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyList.html#method.iter_x)| over elements in an arbitrary order, which is often faster when the order is not required |
79
+
|||
75
80
76
81
As typical, above-mentioned methods have the "_mut" suffixed versions for iterating over mutable references.
Due to the feature of the [`Recursive`](https://docs.rs/orx-split-vec/3.8.0/orx_split_vec/struct.Recursive.html) growth strategy of the underlying SplitVec that allows merging vectors and the nature of linked lists, appending two lists is a constant time operation.
119
124
120
125
See [`append_front`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyList.html#method.append_front) and [`append_back`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyList.html#method.append_back).
[`DoublyIdx<T>`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyIdx.html) and [`SinglyIdx<T>`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.SinglyIdx.html) are the node indices for doubly and singly linked lists, respectively.
144
149
145
-
A node index is analogous to `usize` for a slice (`&[T]`) in the following:
150
+
A node index is analogous to **usize for a slice** (`&[T]`) in the following:
146
151
* It provides constant time access to any element in the list.
147
152
148
-
It differs from `usize` for a slice due to the following:
153
+
It differs from **usize for a slice** due to the following:
149
154
*`usize` represents a position of the slice. Say we have the slice `['a', 'b', 'c']`. Currently, index **0** points to element `a`. However, if we swap the first and third elements, index **0** will now be pointing to `c` because the `usize` represents a position on the slice.
150
155
* A node index represents the element it is created for. Say we now have a list `['a', 'b', 'c']` instead and `idx_a` is the index of the first element. It will always be pointing to this element no matter how many times we change its position, its value, etc.
151
156
152
157
Knowing the index of an element enables a large number of constant time operations. Below is a toy example to demonstrate how the index represents an element rather than a position, and illustrates some of the possible O(1) methods using it.
Alternatively, we can collect all indices at once using the [`indices`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.indices) method.
### Efficiency of Constant Time Mutations (Example)
316
321
317
322
How important are the additional O(1) methods?
318
-
* In this talk [here](https://www.youtube.com/watch?v=YQs6IC-vgmo), Bjarne Stroustrup explains why we should avoid linked list. The talk nicely summarizes the trouble of achieving the appealing constant time mutation promise of linked list. Further, additional memory requirement due to the storage of links or pointers is mentioned.
323
+
* In this talk [here](https://www.youtube.com/watch?v=YQs6IC-vgmo), Bjarne Stroustrup explains why we should avoid linked lists. The talk nicely summarizes the trouble of achieving the appealing constant time mutation promise of linked list. Further, additional memory requirement due to the storage of links or pointers is mentioned.
319
324
* Likewise, there exists the following note in the documentation of the `std::collections::LinkedList`: "It is almost always better to use Vec or VecDeque because array-based containers are generally faster, more memory efficient, and make better use of CPU cache."
320
325
321
326
This crate aims to overcome the concerns with the following approach:
@@ -366,14 +371,7 @@ impl TourLinkedList {
366
371
367
372
Although clear from the worst time complexity of the implementations, [doubly_shuffling_around.rs](https://github.com/orxfun/orx-linked-list/blob/main/benches/doubly_shuffling_around.rs) benchmark demonstrates the dramatic difference. At each setting, we perform 10k `insert_after` moves with tours of different lengths. The following table summarizes the required time in microseconds for each setting.
0 commit comments