Skip to content

Commit f3e0900

Browse files
authored
Enhance docs, add tests in CursorLinkedList (#5994)
1 parent 3a8b04a commit f3e0900

File tree

3 files changed

+196
-59
lines changed

3 files changed

+196
-59
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,7 @@
830830
* lists
831831
* [CircleLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/CircleLinkedListTest.java)
832832
* [CreateAndDetectLoopTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoopTest.java)
833+
* [CursorLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/CursorLinkedListTest.java)
833834
* [MergeKSortedLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedListTest.java)
834835
* [MergeSortedArrayListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedArrayListTest.java)
835836
* [MergeSortedSinglyLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedSinglyLinkedListTest.java)

src/main/java/com/thealgorithms/datastructures/lists/CursorLinkedList.java

Lines changed: 83 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@
33
import java.util.Objects;
44

55
/**
6-
* This class implements a Cursor Linked List.
7-
*
8-
* A CursorLinkedList is an array version of a Linked List. Essentially you have
9-
* an array of list nodes but instead of each node containing a pointer to the
10-
* next item in the linked list, each node element in the array contains the
11-
* index for the next node element.
6+
* CursorLinkedList is an array-based implementation of a singly linked list.
7+
* Each node in the array simulates a linked list node, storing an element and
8+
* the index of the next node. This structure allows for efficient list operations
9+
* without relying on traditional pointers.
1210
*
11+
* @param <T> the type of elements in this list
1312
*/
1413
public class CursorLinkedList<T> {
1514

15+
/**
16+
* Node represents an individual element in the list, containing the element
17+
* itself and a pointer (index) to the next node.
18+
*/
1619
private static class Node<T> {
17-
1820
T element;
1921
int next;
2022

@@ -31,20 +33,26 @@ private static class Node<T> {
3133
private static final int CURSOR_SPACE_SIZE = 100;
3234

3335
{
34-
// init at loading time
36+
// Initialize cursor space array and free list pointers
3537
cursorSpace = new Node[CURSOR_SPACE_SIZE];
3638
for (int i = 0; i < CURSOR_SPACE_SIZE; i++) {
3739
cursorSpace[i] = new Node<>(null, i + 1);
3840
}
3941
cursorSpace[CURSOR_SPACE_SIZE - 1].next = 0;
4042
}
4143

44+
/**
45+
* Constructs an empty CursorLinkedList with the default capacity.
46+
*/
4247
public CursorLinkedList() {
4348
os = 0;
4449
count = 0;
4550
head = -1;
4651
}
4752

53+
/**
54+
* Prints all elements in the list in their current order.
55+
*/
4856
public void printList() {
4957
if (head != -1) {
5058
int start = head;
@@ -57,27 +65,36 @@ public void printList() {
5765
}
5866

5967
/**
60-
* @return the logical index of the element within the list , not the actual
61-
* index of the [cursorSpace] array
68+
* Finds the logical index of a specified element in the list.
69+
*
70+
* @param element the element to search for in the list
71+
* @return the logical index of the element, or -1 if not found
72+
* @throws NullPointerException if element is null
6273
*/
6374
public int indexOf(T element) {
64-
Objects.requireNonNull(element);
65-
Node<T> iterator = cursorSpace[head];
66-
for (int i = 0; i < count; i++) {
67-
if (iterator.element.equals(element)) {
68-
return i;
75+
if (element == null) {
76+
throw new NullPointerException("Element cannot be null");
77+
}
78+
try {
79+
Objects.requireNonNull(element);
80+
Node<T> iterator = cursorSpace[head];
81+
for (int i = 0; i < count; i++) {
82+
if (iterator.element.equals(element)) {
83+
return i;
84+
}
85+
iterator = cursorSpace[iterator.next];
6986
}
70-
iterator = cursorSpace[iterator.next];
87+
} catch (Exception e) {
88+
return -1;
7189
}
72-
7390
return -1;
7491
}
7592

7693
/**
77-
* @param position , the logical index of the element , not the actual one
78-
* within the [cursorSpace] array . this method should be used to get the
79-
* index give by indexOf() method.
80-
* @return
94+
* Retrieves an element at a specified logical index in the list.
95+
*
96+
* @param position the logical index of the element
97+
* @return the element at the specified position, or null if index is out of bounds
8198
*/
8299
public T get(int position) {
83100
if (position >= 0 && position < count) {
@@ -88,51 +105,76 @@ public T get(int position) {
88105
if (counter == position) {
89106
return element;
90107
}
91-
92108
start = cursorSpace[start].next;
93109
counter++;
94110
}
95111
}
96-
97112
return null;
98113
}
99114

115+
/**
116+
* Removes the element at a specified logical index from the list.
117+
*
118+
* @param index the logical index of the element to remove
119+
*/
100120
public void removeByIndex(int index) {
101121
if (index >= 0 && index < count) {
102122
T element = get(index);
103123
remove(element);
104124
}
105125
}
106126

127+
/**
128+
* Removes a specified element from the list.
129+
*
130+
* @param element the element to be removed
131+
* @throws NullPointerException if element is null
132+
*/
107133
public void remove(T element) {
108134
Objects.requireNonNull(element);
109-
110-
// case element is in the head
111135
T tempElement = cursorSpace[head].element;
112136
int tempNext = cursorSpace[head].next;
113137
if (tempElement.equals(element)) {
114138
free(head);
115139
head = tempNext;
116-
} else { // otherwise cases
140+
} else {
117141
int prevIndex = head;
118142
int currentIndex = cursorSpace[prevIndex].next;
119-
120143
while (currentIndex != -1) {
121144
T currentElement = cursorSpace[currentIndex].element;
122145
if (currentElement.equals(element)) {
123146
cursorSpace[prevIndex].next = cursorSpace[currentIndex].next;
124147
free(currentIndex);
125148
break;
126149
}
127-
128150
prevIndex = currentIndex;
129151
currentIndex = cursorSpace[prevIndex].next;
130152
}
131153
}
132-
133154
count--;
134155
}
135156

157+
/**
158+
* Allocates a new node index for storing an element.
159+
*
160+
* @return the index of the newly allocated node
161+
* @throws OutOfMemoryError if no space is available in cursor space
162+
*/
163+
private int alloc() {
164+
int availableNodeIndex = cursorSpace[os].next;
165+
if (availableNodeIndex == 0) {
166+
throw new OutOfMemoryError();
167+
}
168+
cursorSpace[os].next = cursorSpace[availableNodeIndex].next;
169+
cursorSpace[availableNodeIndex].next = -1;
170+
return availableNodeIndex;
171+
}
172+
173+
/**
174+
* Releases a node back to the free list.
175+
*
176+
* @param index the index of the node to release
177+
*/
136178
private void free(int index) {
137179
Node<T> osNode = cursorSpace[os];
138180
int osNext = osNode.next;
@@ -141,44 +183,26 @@ private void free(int index) {
141183
cursorSpace[index].next = osNext;
142184
}
143185

186+
/**
187+
* Appends an element to the end of the list.
188+
*
189+
* @param element the element to append
190+
* @throws NullPointerException if element is null
191+
*/
144192
public void append(T element) {
145193
Objects.requireNonNull(element);
146194
int availableIndex = alloc();
147195
cursorSpace[availableIndex].element = element;
148-
149196
if (head == -1) {
150197
head = availableIndex;
198+
} else {
199+
int iterator = head;
200+
while (cursorSpace[iterator].next != -1) {
201+
iterator = cursorSpace[iterator].next;
202+
}
203+
cursorSpace[iterator].next = availableIndex;
151204
}
152-
153-
int iterator = head;
154-
while (cursorSpace[iterator].next != -1) {
155-
iterator = cursorSpace[iterator].next;
156-
}
157-
158-
cursorSpace[iterator].next = availableIndex;
159205
cursorSpace[availableIndex].next = -1;
160-
161206
count++;
162207
}
163-
164-
/**
165-
* @return the index of the next available node
166-
*/
167-
private int alloc() {
168-
// 1- get the index at which the os is pointing
169-
int availableNodeIndex = cursorSpace[os].next;
170-
171-
if (availableNodeIndex == 0) {
172-
throw new OutOfMemoryError();
173-
}
174-
175-
// 2- make the os point to the next of the @var{availableNodeIndex}
176-
cursorSpace[os].next = cursorSpace[availableNodeIndex].next;
177-
178-
// this to indicate an end of the list , helpful at testing since any err
179-
// would throw an outOfBoundException
180-
cursorSpace[availableNodeIndex].next = -1;
181-
182-
return availableNodeIndex;
183-
}
184208
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package com.thealgorithms.datastructures.lists;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertNull;
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
6+
7+
import org.junit.jupiter.api.BeforeEach;
8+
import org.junit.jupiter.api.Test;
9+
10+
class CursorLinkedListTest {
11+
private CursorLinkedList<String> list;
12+
13+
@BeforeEach
14+
void setUp() {
15+
list = new CursorLinkedList<>();
16+
}
17+
18+
@Test
19+
void testAppendAndGet() {
20+
list.append("First");
21+
list.append("Second");
22+
list.append("Third");
23+
24+
assertEquals("First", list.get(0));
25+
assertEquals("Second", list.get(1));
26+
assertEquals("Third", list.get(2));
27+
assertNull(list.get(3));
28+
assertNull(list.get(-1));
29+
}
30+
31+
@Test
32+
void testIndexOf() {
33+
list.append("First");
34+
list.append("Second");
35+
list.append("Third");
36+
37+
assertEquals(0, list.indexOf("First"));
38+
assertEquals(1, list.indexOf("Second"));
39+
assertEquals(2, list.indexOf("Third"));
40+
assertEquals(-1, list.indexOf("NonExistent"));
41+
}
42+
43+
@Test
44+
void testRemove() {
45+
list.append("First");
46+
list.append("Second");
47+
list.append("Third");
48+
49+
list.remove("Second");
50+
assertEquals("First", list.get(0));
51+
assertEquals("Third", list.get(1));
52+
assertNull(list.get(2));
53+
assertEquals(-1, list.indexOf("Second"));
54+
}
55+
56+
@Test
57+
void testRemoveByIndex() {
58+
list.append("First");
59+
list.append("Second");
60+
list.append("Third");
61+
62+
list.removeByIndex(1);
63+
assertEquals("First", list.get(0));
64+
assertEquals("Third", list.get(1));
65+
assertNull(list.get(2));
66+
}
67+
68+
@Test
69+
void testRemoveFirstElement() {
70+
list.append("First");
71+
list.append("Second");
72+
73+
list.remove("First");
74+
assertEquals("Second", list.get(0));
75+
assertNull(list.get(1));
76+
assertEquals(-1, list.indexOf("First"));
77+
}
78+
79+
@Test
80+
void testRemoveLastElement() {
81+
list.append("First");
82+
list.append("Second");
83+
84+
list.remove("Second");
85+
assertEquals("First", list.get(0));
86+
assertNull(list.get(1));
87+
assertEquals(-1, list.indexOf("Second"));
88+
}
89+
90+
@Test
91+
void testNullHandling() {
92+
assertThrows(NullPointerException.class, () -> list.append(null));
93+
assertThrows(NullPointerException.class, () -> list.remove(null));
94+
assertThrows(NullPointerException.class, () -> list.indexOf(null));
95+
}
96+
97+
@Test
98+
void testEmptyList() {
99+
assertNull(list.get(0));
100+
assertEquals(-1, list.indexOf("Any"));
101+
}
102+
103+
@Test
104+
void testMemoryLimitExceeded() {
105+
// Test adding more elements than CURSOR_SPACE_SIZE
106+
assertThrows(OutOfMemoryError.class, () -> {
107+
for (int i = 0; i < 101; i++) { // CURSOR_SPACE_SIZE is 100
108+
list.append("Element" + i);
109+
}
110+
});
111+
}
112+
}

0 commit comments

Comments
 (0)