Repository: TheAlgorithms/Java
Pull request #5352 (2024-08-20 21:47:19)

refactor: `LinkedQueue`
Make LinkedQueue generic.
Readability improvements.
Added tests.

<!--
Thank you for your contribution!
In order to reduce the number of notifications sent to the maintainers, please:
- create your PR as draft, cf. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests,
- make sure that all of the CI checks pass,
- mark your PR as ready for review, cf. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request#marking-a-pull-request-as-ready-for-review
-->

<!-- For completed items, change [ ] to [x] -->

- [x] I have read [CONTRIBUTING.md](https://github.com/TheAlgorithms/Java/blob/master/CONTRIBUTING.md).
- [x] This pull request is all my own work -- I have not plagiarized it.
- [x] All filenames are in PascalCase.
- [x] All functions and variable names follow Java naming conventions.
- [x] All new algorithms have a URL in their comments that points to Wikipedia or other similar explanations.
- [x] All new code is formatted with `clang-format -i --style=file path/to/your/file.java`

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/TheAlgorithms/Java/pull/5352?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 44.55%. Comparing base [(`f5c0314`)](https://app.codecov.io/gh/TheAlgorithms/Java/commit/f5c0314111f5973748862e21d6e7ccbb9e53a3ee?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms) to head [(`7abf2bc`)](https://app.codecov.io/gh/TheAlgorithms/Java/commit/7abf2bc9195e92c9f36ee6caa8e6052c25f7bd62?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms).

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff              @@
##             master    #5352      +/-   ##
============================================
+ Coverage     44.38%   44.55%   +0.17%     
- Complexity     2752     2767      +15     
============================================
  Files           522      522              
  Lines         15419    15427       +8     
  Branches       2942     2944       +2     
============================================
+ Hits           6843     6874      +31     
+ Misses         8282     8260      -22     
+ Partials        294      293       -1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/TheAlgorithms/Java/pull/5352?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms).


Patch:
diff --git a/src/main/java/com/thealgorithms/datastructures/queues/LinkedQueue.java b/src/main/java/com/thealgorithms/datastructures/queues/LinkedQueue.java
index 5fba2ff6..6ba16199 100644
--- a/src/main/java/com/thealgorithms/datastructures/queues/LinkedQueue.java
+++ b/src/main/java/com/thealgorithms/datastructures/queues/LinkedQueue.java
@@ -2,216 +2,200 @@ package com.thealgorithms.datastructures.queues;
 
 import java.util.Iterator;
 import java.util.NoSuchElementException;
-import java.util.StringJoiner;
 
 public class LinkedQueue<T> implements Iterable<T> {
 
-    static class Node<T> {
-
+    /**
+     * Node class representing each element in the queue.
+     */
+    private static class Node<T> {
         T data;
         Node<T> next;
 
-        Node() {
-            this(null);
-        }
-
         Node(T data) {
-            this(data, null);
-        }
-
-        Node(T data, Node<T> next) {
             this.data = data;
-            this.next = next;
+            this.next = null;
         }
     }
 
-    /**
-     * Front of Queue
-     */
-    private Node<T> front;
-
-    /**
-     * Rear of Queue
-     */
-    private Node<T> rear;
+    private Node<T> front; // Front of the queue
+    private Node<T> rear; // Rear of the queue
+    private int size; // Size of the queue
 
     /**
-     * Size of Queue
-     */
-    private int size;
-
-    /**
-     * Init LinkedQueue
+     * Initializes an empty LinkedQueue.
      */
     public LinkedQueue() {
-
-        front = new Node<>();
-        rear = front;
+        front = null;
+        rear = null;
+        size = 0;
     }
 
     /**
-     * Check if queue is empty
+     * Checks if the queue is empty.
      *
-     * @return true if queue is empty, otherwise false
+     * @return true if the queue is empty, otherwise false.
      */
     public boolean isEmpty() {
         return size == 0;
     }
 
     /**
-     * Add element to rear of queue
+     * Adds an element to the rear of the queue.
      *
-     * @param data insert value
+     * @param data the element to insert.
+     * @throws IllegalArgumentException if data is null.
      */
     public void enqueue(T data) {
+        if (data == null) {
+            throw new IllegalArgumentException("Cannot enqueue null data");
+        }
+
         Node<T> newNode = new Node<>(data);
 
-        rear.next = newNode;
+        if (isEmpty()) {
+            front = newNode;
+        } else {
+            rear.next = newNode;
+        }
         rear = newNode;
-        /* make rear point at last node */
         size++;
     }
 
     /**
-     * Remove element at the front of queue
+     * Removes and returns the element at the front of the queue.
      *
-     * @return element at the front of queue
+     * @return the element at the front of the queue.
+     * @throws NoSuchElementException if the queue is empty.
      */
     public T dequeue() {
         if (isEmpty()) {
-            throw new NoSuchElementException("queue is empty");
+            throw new NoSuchElementException("Queue is empty");
         }
-        Node<T> destroy = front.next;
-        T retValue = destroy.data;
-        front.next = front.next.next;
-        /* clear let GC do it's work */
+
+        T retValue = front.data;
+        front = front.next;
         size--;
 
         if (isEmpty()) {
-            front = rear;
+            rear = null;
         }
 
         return retValue;
     }
 
     /**
-     * Peek element at the front of queue without removing
+     * Returns the element at the front of the queue without removing it.
      *
-     * @return element at the front
+     * @return the element at the front of the queue.
+     * @throws NoSuchElementException if the queue is empty.
      */
     public T peekFront() {
         if (isEmpty()) {
-            throw new NoSuchElementException("queue is empty");
+            throw new NoSuchElementException("Queue is empty");
         }
-        return front.next.data;
+        return front.data;
     }
 
     /**
-     * Peek element at the rear of queue without removing
+     * Returns the element at the rear of the queue without removing it.
      *
-     * @return element at the front
+     * @return the element at the rear of the queue.
+     * @throws NoSuchElementException if the queue is empty.
      */
     public T peekRear() {
         if (isEmpty()) {
-            throw new NoSuchElementException("queue is empty");
+            throw new NoSuchElementException("Queue is empty");
         }
         return rear.data;
     }
 
     /**
-     * Peeks the element at the index and
-     *          returns the value
-     * @param pos at which to peek
+     * Returns the element at the specified position (1-based index).
+     *
+     * @param pos the position to peek at (1-based index).
+     * @return the element at the specified position.
+     * @throws IndexOutOfBoundsException if the position is out of range.
      */
-
     public T peek(int pos) {
-        if (pos > size) {
-            throw new IndexOutOfBoundsException("Position %s out of range!".formatted(pos));
+        if (pos < 1 || pos > size) {
+            throw new IndexOutOfBoundsException("Position " + pos + " out of range!");
         }
+
         Node<T> node = front;
-        while (pos-- > 0) {
+        for (int i = 1; i < pos; i++) {
             node = node.next;
         }
         return node.data;
     }
 
     /**
-     * Node iterator, allows to travel through
-     * the nodes using for() loop or forEach(Consumer)
+     * Returns an iterator over the elements in the queue.
+     *
+     * @return an iterator over the elements in the queue.
      */
-
     @Override
     public Iterator<T> iterator() {
         return new Iterator<>() {
-            Node<T> node = front;
+            private Node<T> current = front;
 
             @Override
             public boolean hasNext() {
-                return node.next != null;
+                return current != null;
             }
 
             @Override
             public T next() {
-                if (hasNext()) {
-                    node = node.next;
-                    return node.data;
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
                 }
-                throw new NoSuchElementException();
+
+                T data = current.data;
+                current = current.next;
+                return data;
             }
         };
     }
 
     /**
-     * Return size of queue
+     * Returns the size of the queue.
      *
-     * @return size of queue
+     * @return the size of the queue.
      */
     public int size() {
         return size;
     }
 
     /**
-     * Clear all nodes in queue
+     * Clears all elements from the queue.
      */
     public void clear() {
-        while (size > 0) {
-            dequeue();
-        }
+        front = null;
+        rear = null;
+        size = 0;
     }
 
+    /**
+     * Returns a string representation of the queue.
+     *
+     * @return a string representation of the queue.
+     */
     @Override
     public String toString() {
-        StringJoiner join = new StringJoiner(", "); // separator of ', '
-        Node<T> travel = front;
-        while ((travel = travel.next) != null) {
-            join.add(String.valueOf(travel.data));
+        if (isEmpty()) {
+            return "[]";
         }
-        return '[' + join.toString() + ']';
-    }
 
-    /* Driver Code */
-    public static void main(String[] args) {
-        LinkedQueue<Integer> queue = new LinkedQueue<>();
-        assert queue.isEmpty();
-
-        queue.enqueue(1);
-        /* 1 */
-        queue.enqueue(2);
-        /* 1 2 */
-        queue.enqueue(3);
-        /* 1 2 3 */
-        System.out.println(queue);
-        /* [1, 2, 3] */
-
-        assert queue.size() == 3;
-        assert queue.dequeue() == 1;
-        assert queue.peekFront() == 2;
-        assert queue.peekRear() == 3;
-
-        queue.clear();
-        assert queue.isEmpty();
-
-        System.out.println(queue);
-        /* [] */
+        StringBuilder sb = new StringBuilder("[");
+        Node<T> current = front;
+        while (current != null) {
+            sb.append(current.data);
+            if (current.next != null) {
+                sb.append(", ");
+            }
+            current = current.next;
+        }
+        sb.append(']');
+        return sb.toString();
     }
 }


Test patch:
diff --git a/src/test/java/com/thealgorithms/datastructures/queues/LinkedQueueTest.java b/src/test/java/com/thealgorithms/datastructures/queues/LinkedQueueTest.java
index 7bebf13e..157d771b 100644
--- a/src/test/java/com/thealgorithms/datastructures/queues/LinkedQueueTest.java
+++ b/src/test/java/com/thealgorithms/datastructures/queues/LinkedQueueTest.java
@@ -1,30 +1,209 @@
 package com.thealgorithms.datastructures.queues;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 class LinkedQueueTest {
+
+    private LinkedQueue<Integer> queue;
+
+    @BeforeEach
+    void setUp() {
+        queue = new LinkedQueue<>();
+    }
+
+    @Test
+    void testIsEmptyOnNewQueue() {
+        assertTrue(queue.isEmpty(), "Queue should be empty on initialization.");
+    }
+
+    @Test
+    void testEnqueueAndSize() {
+        queue.enqueue(10);
+        assertFalse(queue.isEmpty(), "Queue should not be empty after enqueue.");
+        assertEquals(1, queue.size(), "Queue size should be 1 after one enqueue.");
+
+        queue.enqueue(20);
+        queue.enqueue(30);
+        assertEquals(3, queue.size(), "Queue size should be 3 after three enqueues.");
+    }
+
+    @Test
+    void testDequeueOnSingleElementQueue() {
+        queue.enqueue(10);
+        assertEquals(10, queue.dequeue(), "Dequeued element should be the same as the enqueued one.");
+        assertTrue(queue.isEmpty(), "Queue should be empty after dequeueing the only element.");
+    }
+
+    @Test
+    void testDequeueMultipleElements() {
+        queue.enqueue(10);
+        queue.enqueue(20);
+        queue.enqueue(30);
+
+        assertEquals(10, queue.dequeue(), "First dequeued element should be the first enqueued one.");
+        assertEquals(20, queue.dequeue(), "Second dequeued element should be the second enqueued one.");
+        assertEquals(30, queue.dequeue(), "Third dequeued element should be the third enqueued one.");
+        assertTrue(queue.isEmpty(), "Queue should be empty after dequeueing all elements.");
+    }
+
+    @Test
+    void testDequeueOnEmptyQueue() {
+        org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, () -> queue.dequeue(), "Dequeueing from an empty queue should throw NoSuchElementException.");
+    }
+
+    @Test
+    void testPeekFrontOnEmptyQueue() {
+        org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, () -> queue.peekFront(), "Peeking front on an empty queue should throw NoSuchElementException.");
+    }
+
     @Test
-    public void testQue() {
-        LinkedQueue<Integer> queue = new LinkedQueue<>();
-        for (int i = 1; i < 5; i++) {
+    void testPeekRearOnEmptyQueue() {
+        org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, () -> queue.peekRear(), "Peeking rear on an empty queue should throw NoSuchElementException.");
+    }
+
+    @Test
+    void testPeekFront() {
+        queue.enqueue(10);
+        queue.enqueue(20);
+        queue.enqueue(30);
+
+        assertEquals(10, queue.peekFront(), "Peek front should return the first enqueued element.");
+        assertEquals(10, queue.peekFront(), "Peek front should not remove the element.");
+        assertEquals(3, queue.size(), "Queue size should remain unchanged after peek.");
+    }
+
+    @Test
+    void testPeekRear() {
+        queue.enqueue(10);
+        queue.enqueue(20);
+        queue.enqueue(30);
+
+        assertEquals(30, queue.peekRear(), "Peek rear should return the last enqueued element.");
+        assertEquals(30, queue.peekRear(), "Peek rear should not remove the element.");
+        assertEquals(3, queue.size(), "Queue size should remain unchanged after peek.");
+    }
+
+    @Test
+    void testPeekAtPosition() {
+        queue.enqueue(10);
+        queue.enqueue(20);
+        queue.enqueue(30);
+
+        assertEquals(10, queue.peek(1), "Peek at position 1 should return the first enqueued element.");
+        assertEquals(20, queue.peek(2), "Peek at position 2 should return the second enqueued element.");
+        assertEquals(30, queue.peek(3), "Peek at position 3 should return the third enqueued element.");
+    }
+
+    @Test
+    void testPeekAtInvalidPosition() {
+        queue.enqueue(10);
+        queue.enqueue(20);
+        queue.enqueue(30);
+
+        org.junit.jupiter.api.Assertions.assertThrows(IndexOutOfBoundsException.class, () -> queue.peek(4), "Peeking at a position greater than size should throw IndexOutOfBoundsException.");
+        org.junit.jupiter.api.Assertions.assertThrows(IndexOutOfBoundsException.class, () -> queue.peek(0), "Peeking at position 0 should throw IndexOutOfBoundsException.");
+    }
+
+    @Test
+    void testClearQueue() {
+        queue.enqueue(10);
+        queue.enqueue(20);
+        queue.enqueue(30);
+
+        queue.clear();
+        assertTrue(queue.isEmpty(), "Queue should be empty after clear.");
+        assertEquals(0, queue.size(), "Queue size should be 0 after clear.");
+    }
+
+    @Test
+    void testIterator() {
+        queue.enqueue(10);
+        queue.enqueue(20);
+        queue.enqueue(30);
+
+        Iterator<Integer> it = queue.iterator();
+        assertTrue(it.hasNext(), "Iterator should have next element.");
+        assertEquals(10, it.next(), "First iterator value should be the first enqueued element.");
+        assertEquals(20, it.next(), "Second iterator value should be the second enqueued element.");
+        assertEquals(30, it.next(), "Third iterator value should be the third enqueued element.");
+        assertFalse(it.hasNext(), "Iterator should not have next element after last element.");
+        org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, it::next, "Calling next() on exhausted iterator should throw NoSuchElementException.");
+    }
+
+    @Test
+    void testToString() {
+        queue.enqueue(10);
+        queue.enqueue(20);
+        queue.enqueue(30);
+
+        assertEquals("[10, 20, 30]", queue.toString(), "toString should return a properly formatted string representation of the queue.");
+    }
+
+    @Test
+    void testEnqueueAfterDequeue() {
+        queue.enqueue(10);
+        queue.enqueue(20);
+        queue.enqueue(30);
+
+        assertEquals(10, queue.dequeue(), "Dequeued element should be 10.");
+        assertEquals(20, queue.dequeue(), "Dequeued element should be 20.");
+
+        queue.enqueue(40);
+        assertEquals(30, queue.peekFront(), "Peek front should return 30 after dequeuing and enqueuing new elements.");
+        assertEquals(40, queue.peekRear(), "Peek rear should return 40 after enqueuing new elements.");
+    }
+
+    @Test
+    void testQueueMaintainsOrder() {
+        for (int i = 1; i <= 100; i++) {
             queue.enqueue(i);
         }
 
-        assertEquals(queue.peekRear(), 4);
-        assertEquals(queue.peek(2), 2);
+        for (int i = 1; i <= 100; i++) {
+            assertEquals(i, queue.dequeue(), "Queue should maintain the correct order of elements.");
+        }
+
+        assertTrue(queue.isEmpty(), "Queue should be empty after dequeuing all elements.");
+    }
+
+    @Test
+    void testSizeAfterOperations() {
+        assertEquals(0, queue.size(), "Initial queue size should be 0.");
+
+        queue.enqueue(10);
+        assertEquals(1, queue.size(), "Queue size should be 1 after one enqueue.");
 
-        assertEquals(queue.peek(4), 4);
+        queue.enqueue(20);
+        assertEquals(2, queue.size(), "Queue size should be 2 after two enqueues.");
 
-        final int[] element = {1};
+        queue.dequeue();
+        assertEquals(1, queue.size(), "Queue size should be 1 after one dequeue.");
 
-        // iterates over all the elements present
-        // as in the form of nodes
-        queue.forEach(integer -> {
-            if (element[0]++ != integer) {
-                throw new AssertionError();
-            }
-        });
+        queue.clear();
+        assertEquals(0, queue.size(), "Queue size should be 0 after clear.");
+    }
+
+    @Test
+    void testQueueToStringOnEmptyQueue() {
+        assertEquals("[]", queue.toString(), "toString on empty queue should return '[]'.");
+    }
+
+    @Test
+    void testEnqueueNull() {
+        org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> queue.enqueue(null), "Cannot enqueue null data.");
+    }
+
+    @Test
+    void testIteratorOnEmptyQueue() {
+        Iterator<Integer> it = queue.iterator();
+        assertFalse(it.hasNext(), "Iterator on empty queue should not have next element.");
+        org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, it::next, "Calling next() on empty queue iterator should throw NoSuchElementException.");
     }
 }