Skip to content

Commit 202879a

Browse files
authored
Enhance docs, add tests in CircleLinkedList (#5991)
1 parent c766c5e commit 202879a

File tree

2 files changed

+114
-46
lines changed

2 files changed

+114
-46
lines changed

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

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
11
package com.thealgorithms.datastructures.lists;
22

3+
/**
4+
* This class is a circular singly linked list implementation. In a circular linked list,
5+
* the last node points back to the first node, creating a circular chain.
6+
*
7+
* <p>This implementation includes basic operations such as appending elements
8+
* to the end, removing elements from a specified position, and converting
9+
* the list to a string representation.
10+
*
11+
* @param <E> the type of elements held in this list
12+
*/
313
public class CircleLinkedList<E> {
414

5-
private static final class Node<E> {
15+
/**
16+
* A static nested class representing a node in the circular linked list.
17+
*
18+
* @param <E> the type of element stored in the node
19+
*/
20+
static final class Node<E> {
621

722
Node<E> next;
823
E value;
@@ -13,44 +28,56 @@ private Node(E value, Node<E> next) {
1328
}
1429
}
1530

16-
// For better O.O design this should be private allows for better black box design
1731
private int size;
18-
// this will point to dummy node;
19-
private Node<E> head = null;
20-
private Node<E> tail = null; // keeping a tail pointer to keep track of the end of list
32+
Node<E> head = null;
33+
private Node<E> tail;
2134

22-
// constructor for class.. here we will make a dummy node for circly linked list implementation
23-
// with reduced error catching as our list will never be empty;
35+
/**
36+
* Initializes a new circular linked list. A dummy head node is used for simplicity,
37+
* pointing initially to itself to ensure the list is never empty.
38+
*/
2439
public CircleLinkedList() {
25-
// creation of the dummy node
26-
head = new Node<E>(null, head);
40+
head = new Node<>(null, head);
2741
tail = head;
2842
size = 0;
2943
}
3044

31-
// getter for the size... needed because size is private.
45+
/**
46+
* Returns the current size of the list.
47+
*
48+
* @return the number of elements in the list
49+
*/
3250
public int getSize() {
3351
return size;
3452
}
3553

36-
// for the sake of simplistiy this class will only contain the append function or addLast other
37-
// add functions can be implemented however this is the basses of them all really.
54+
/**
55+
* Appends a new element to the end of the list. Throws a NullPointerException if
56+
* a null value is provided.
57+
*
58+
* @param value the value to append to the list
59+
* @throws NullPointerException if the value is null
60+
*/
3861
public void append(E value) {
3962
if (value == null) {
40-
// we do not want to add null elements to the list.
4163
throw new NullPointerException("Cannot add null element to the list");
4264
}
43-
// head.next points to the last element;
4465
if (tail == null) {
45-
tail = new Node<E>(value, head);
66+
tail = new Node<>(value, head);
4667
head.next = tail;
4768
} else {
48-
tail.next = new Node<E>(value, head);
69+
tail.next = new Node<>(value, head);
4970
tail = tail.next;
5071
}
5172
size++;
5273
}
5374

75+
/**
76+
* Returns a string representation of the list in the format "[ element1, element2, ... ]".
77+
* An empty list is represented as "[]".
78+
*
79+
* @return the string representation of the list
80+
*/
5481
public String toString() {
5582
if (size == 0) {
5683
return "[]";
@@ -68,23 +95,27 @@ public String toString() {
6895
return sb.toString();
6996
}
7097

98+
/**
99+
* Removes and returns the element at the specified position in the list.
100+
* Throws an IndexOutOfBoundsException if the position is invalid.
101+
*
102+
* @param pos the position of the element to remove
103+
* @return the value of the removed element
104+
* @throws IndexOutOfBoundsException if the position is out of range
105+
*/
71106
public E remove(int pos) {
72107
if (pos >= size || pos < 0) {
73-
// catching errors
74-
throw new IndexOutOfBoundsException("position cannot be greater than size or negative");
108+
throw new IndexOutOfBoundsException("Position out of bounds");
75109
}
76-
// we need to keep track of the element before the element we want to remove we can see why
77-
// bellow.
110+
78111
Node<E> before = head;
79112
for (int i = 1; i <= pos; i++) {
80113
before = before.next;
81114
}
82115
Node<E> destroy = before.next;
83116
E saved = destroy.value;
84-
// assigning the next reference to the element following the element we want to remove...
85-
// the last element will be assigned to the head.
86-
before.next = before.next.next;
87-
// scrubbing
117+
before.next = destroy.next;
118+
88119
if (destroy == tail) {
89120
tail = before;
90121
}
Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,115 @@
11
package com.thealgorithms.datastructures.lists;
22

33
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertSame;
45
import static org.junit.jupiter.api.Assertions.assertThrows;
56

7+
import org.junit.jupiter.api.BeforeEach;
68
import org.junit.jupiter.api.Test;
79

810
public class CircleLinkedListTest {
911

12+
private CircleLinkedList<Integer> list;
13+
14+
@BeforeEach
15+
public void setUp() {
16+
list = new CircleLinkedList<>();
17+
}
18+
19+
@Test
20+
public void testInitialSize() {
21+
assertEquals(0, list.getSize(), "Initial size should be 0.");
22+
}
23+
1024
@Test
1125
public void testAppendAndSize() {
12-
CircleLinkedList<Integer> list = new CircleLinkedList<>();
1326
list.append(1);
1427
list.append(2);
1528
list.append(3);
1629

17-
assertEquals(3, list.getSize());
18-
assertEquals("[ 1, 2, 3 ]", list.toString());
30+
assertEquals(3, list.getSize(), "Size after three appends should be 3.");
31+
assertEquals("[ 1, 2, 3 ]", list.toString(), "List content should match appended values.");
1932
}
2033

2134
@Test
2235
public void testRemove() {
23-
CircleLinkedList<Integer> list = new CircleLinkedList<>();
2436
list.append(1);
2537
list.append(2);
2638
list.append(3);
2739
list.append(4);
2840

29-
assertEquals(2, list.remove(1));
30-
assertEquals(3, list.remove(1));
31-
assertEquals("[ 1, 4 ]", list.toString());
32-
assertEquals(2, list.getSize());
41+
assertEquals(2, list.remove(1), "Removed element at index 1 should be 2.");
42+
assertEquals(3, list.remove(1), "Removed element at index 1 after update should be 3.");
43+
assertEquals("[ 1, 4 ]", list.toString(), "List content should reflect removals.");
44+
assertEquals(2, list.getSize(), "Size after two removals should be 2.");
3345
}
3446

3547
@Test
3648
public void testRemoveInvalidIndex() {
37-
CircleLinkedList<Integer> list = new CircleLinkedList<>();
3849
list.append(1);
3950
list.append(2);
4051

41-
assertThrows(IndexOutOfBoundsException.class, () -> list.remove(2));
42-
assertThrows(IndexOutOfBoundsException.class, () -> list.remove(-1));
52+
assertThrows(IndexOutOfBoundsException.class, () -> list.remove(2), "Should throw on out-of-bounds index.");
53+
assertThrows(IndexOutOfBoundsException.class, () -> list.remove(-1), "Should throw on negative index.");
4354
}
4455

4556
@Test
4657
public void testToStringEmpty() {
47-
CircleLinkedList<Integer> list = new CircleLinkedList<>();
48-
assertEquals("[]", list.toString());
58+
assertEquals("[]", list.toString(), "Empty list should be represented by '[]'.");
4959
}
5060

5161
@Test
5262
public void testToStringAfterRemoval() {
53-
CircleLinkedList<Integer> list = new CircleLinkedList<>();
5463
list.append(1);
5564
list.append(2);
5665
list.append(3);
5766
list.remove(1);
5867

59-
assertEquals("[ 1, 3 ]", list.toString());
68+
assertEquals("[ 1, 3 ]", list.toString(), "List content should match remaining elements after removal.");
6069
}
6170

6271
@Test
6372
public void testSingleElement() {
64-
CircleLinkedList<Integer> list = new CircleLinkedList<>();
6573
list.append(1);
6674

67-
assertEquals(1, list.getSize());
68-
assertEquals("[ 1 ]", list.toString());
69-
assertEquals(1, list.remove(0));
70-
assertEquals("[]", list.toString());
75+
assertEquals(1, list.getSize(), "Size after single append should be 1.");
76+
assertEquals("[ 1 ]", list.toString(), "Single element list should display properly.");
77+
assertEquals(1, list.remove(0), "Single element removed should match appended value.");
78+
assertEquals("[]", list.toString(), "List should be empty after removing the single element.");
7179
}
7280

7381
@Test
7482
public void testNullElement() {
75-
CircleLinkedList<String> list = new CircleLinkedList<>();
76-
assertThrows(NullPointerException.class, () -> list.append(null));
83+
assertThrows(NullPointerException.class, () -> list.append(null), "Appending null should throw exception.");
84+
}
85+
86+
@Test
87+
public void testCircularReference() {
88+
list.append(1);
89+
list.append(2);
90+
list.append(3);
91+
CircleLinkedList.Node<Integer> current = list.head;
92+
93+
// Traverse one full cycle and verify the circular reference
94+
for (int i = 0; i <= list.getSize(); i++) {
95+
current = current.next;
96+
}
97+
assertEquals(list.head, current, "End of list should point back to the head (circular structure).");
98+
}
99+
100+
@Test
101+
public void testClear() {
102+
list.append(1);
103+
list.append(2);
104+
list.append(3);
105+
106+
// Remove all elements to simulate clearing the list
107+
for (int i = list.getSize() - 1; i >= 0; i--) {
108+
list.remove(i);
109+
}
110+
111+
assertEquals(0, list.getSize(), "Size after clearing should be 0.");
112+
assertEquals("[]", list.toString(), "Empty list should be represented by '[]' after clear.");
113+
assertSame(list.head.next, list.head, "Head's next should point to itself after clearing.");
77114
}
78115
}

0 commit comments

Comments
 (0)