Skip to content

Commit 6539d95

Browse files
committed
refactor: Enhance docs, add tests in CursorLinkedList
1 parent 5246f63 commit 6539d95

File tree

2 files changed

+193
-59
lines changed

2 files changed

+193
-59
lines changed

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: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package com.thealgorithms.datastructures.lists;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import org.junit.jupiter.api.BeforeEach;
6+
import org.junit.jupiter.api.Test;
7+
8+
class CursorLinkedListTest {
9+
private CursorLinkedList<String> list;
10+
11+
@BeforeEach
12+
void setUp() {
13+
list = new CursorLinkedList<>();
14+
}
15+
16+
@Test
17+
void testAppendAndGet() {
18+
list.append("First");
19+
list.append("Second");
20+
list.append("Third");
21+
22+
assertEquals("First", list.get(0));
23+
assertEquals("Second", list.get(1));
24+
assertEquals("Third", list.get(2));
25+
assertNull(list.get(3));
26+
assertNull(list.get(-1));
27+
}
28+
29+
@Test
30+
void testIndexOf() {
31+
list.append("First");
32+
list.append("Second");
33+
list.append("Third");
34+
35+
assertEquals(0, list.indexOf("First"));
36+
assertEquals(1, list.indexOf("Second"));
37+
assertEquals(2, list.indexOf("Third"));
38+
assertEquals(-1, list.indexOf("NonExistent"));
39+
}
40+
41+
@Test
42+
void testRemove() {
43+
list.append("First");
44+
list.append("Second");
45+
list.append("Third");
46+
47+
list.remove("Second");
48+
assertEquals("First", list.get(0));
49+
assertEquals("Third", list.get(1));
50+
assertNull(list.get(2));
51+
assertEquals(-1, list.indexOf("Second"));
52+
}
53+
54+
@Test
55+
void testRemoveByIndex() {
56+
list.append("First");
57+
list.append("Second");
58+
list.append("Third");
59+
60+
list.removeByIndex(1);
61+
assertEquals("First", list.get(0));
62+
assertEquals("Third", list.get(1));
63+
assertNull(list.get(2));
64+
}
65+
66+
@Test
67+
void testRemoveFirstElement() {
68+
list.append("First");
69+
list.append("Second");
70+
71+
list.remove("First");
72+
assertEquals("Second", list.get(0));
73+
assertNull(list.get(1));
74+
assertEquals(-1, list.indexOf("First"));
75+
}
76+
77+
@Test
78+
void testRemoveLastElement() {
79+
list.append("First");
80+
list.append("Second");
81+
82+
list.remove("Second");
83+
assertEquals("First", list.get(0));
84+
assertNull(list.get(1));
85+
assertEquals(-1, list.indexOf("Second"));
86+
}
87+
88+
@Test
89+
void testNullHandling() {
90+
assertThrows(NullPointerException.class, () -> list.append(null));
91+
assertThrows(NullPointerException.class, () -> list.remove(null));
92+
assertThrows(NullPointerException.class, () -> list.indexOf(null));
93+
}
94+
95+
@Test
96+
void testEmptyList() {
97+
assertNull(list.get(0));
98+
assertEquals(-1, list.indexOf("Any"));
99+
}
100+
101+
@Test
102+
void testMemoryLimitExceeded() {
103+
// Test adding more elements than CURSOR_SPACE_SIZE
104+
assertThrows(OutOfMemoryError.class, () -> {
105+
for (int i = 0; i < 101; i++) { // CURSOR_SPACE_SIZE is 100
106+
list.append("Element" + i);
107+
}
108+
});
109+
}
110+
}

0 commit comments

Comments
 (0)