Skip to content

refactor: Enhance docs, add more tests in Bag #5947

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 37 additions & 9 deletions src/main/java/com/thealgorithms/datastructures/bags/Bag.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@
import java.util.NoSuchElementException;

/**
* A collection that allows adding and iterating over elements but does not support element removal.
* A generic collection that allows adding and iterating over elements but does not support
* element removal. This class implements a simple bag data structure, which can hold duplicate
* elements and provides operations to check for membership and the size of the collection.
*
* <p>Bag is not thread-safe and should not be accessed by multiple threads concurrently.
*
* @param <E> the type of elements in this bag
*/
public class Bag<E> implements Iterable<E> {

private Node<E> firstElement; // First element in the bag
private int size; // Number of elements in the bag
private Node<E> firstElement; // Reference to the first element in the bag
private int size; // Count of elements in the bag

// Node class representing each element in the bag
private static final class Node<E> {
Expand All @@ -21,6 +25,7 @@ private static final class Node<E> {

/**
* Constructs an empty bag.
* <p>This initializes the bag with zero elements.
*/
public Bag() {
firstElement = null;
Expand All @@ -30,7 +35,7 @@ public Bag() {
/**
* Checks if the bag is empty.
*
* @return true if the bag is empty, false otherwise
* @return {@code true} if the bag contains no elements; {@code false} otherwise
*/
public boolean isEmpty() {
return size == 0;
Expand All @@ -39,7 +44,7 @@ public boolean isEmpty() {
/**
* Returns the number of elements in the bag.
*
* @return the number of elements
* @return the number of elements currently in the bag
*/
public int size() {
return size;
Expand All @@ -48,7 +53,10 @@ public int size() {
/**
* Adds an element to the bag.
*
* @param element the element to add
* <p>This method adds the specified element to the bag. Duplicates are allowed, and the
* bag will maintain the order in which elements are added.
*
* @param element the element to add; must not be {@code null}
*/
public void add(E element) {
Node<E> newNode = new Node<>();
Expand All @@ -61,8 +69,10 @@ public void add(E element) {
/**
* Checks if the bag contains a specific element.
*
* @param element the element to check for
* @return true if the bag contains the element, false otherwise
* <p>This method uses the {@code equals} method of the element to determine membership.
*
* @param element the element to check for; must not be {@code null}
* @return {@code true} if the bag contains the specified element; {@code false} otherwise
*/
public boolean contains(E element) {
for (E value : this) {
Expand All @@ -76,6 +86,8 @@ public boolean contains(E element) {
/**
* Returns an iterator over the elements in this bag.
*
* <p>The iterator provides a way to traverse the elements in the order they were added.
*
* @return an iterator that iterates over the elements in the bag
*/
@Override
Expand All @@ -88,19 +100,35 @@ private static class ListIterator<E> implements Iterator<E> {

private Node<E> currentElement;

/**
* Constructs a ListIterator starting from the given first element.
*
* @param firstElement the first element of the bag to iterate over
*/
ListIterator(Node<E> firstElement) {
this.currentElement = firstElement;
}

/**
* Checks if there are more elements to iterate over.
*
* @return {@code true} if there are more elements; {@code false} otherwise
*/
@Override
public boolean hasNext() {
return currentElement != null;
}

/**
* Returns the next element in the iteration.
*
* @return the next element in the bag
* @throws NoSuchElementException if there are no more elements to return
*/
@Override
public E next() {
if (!hasNext()) {
throw new NoSuchElementException();
throw new NoSuchElementException("No more elements in the bag.");
}
E element = currentElement.content;
currentElement = currentElement.nextElement;
Expand Down
56 changes: 52 additions & 4 deletions src/test/java/com/thealgorithms/datastructures/bag/BagTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.thealgorithms.datastructures.bags.Bag;
Expand Down Expand Up @@ -68,12 +69,12 @@ void testContainsMethod() {
}

@Test
void testContainsAfterRemoveOperation() {
void testContainsAfterAdditions() {
Bag<String> bag = new Bag<>();
bag.add("item1");
bag.add("item2");
assertTrue(bag.contains("item1"), "Bag should contain 'item1' before removal");
assertTrue(bag.contains("item2"), "Bag should contain 'item2' before removal");
assertTrue(bag.contains("item1"), "Bag should contain 'item1' after addition");
assertTrue(bag.contains("item2"), "Bag should contain 'item2' after addition");
}

@Test
Expand Down Expand Up @@ -106,6 +107,53 @@ void testRemoveMethodThrowsException() {
Bag<String> bag = new Bag<>();
bag.add("item1");
Iterator<String> iterator = bag.iterator();
org.junit.jupiter.api.Assertions.assertThrows(UnsupportedOperationException.class, iterator::remove, "Remove operation should throw UnsupportedOperationException");
assertThrows(UnsupportedOperationException.class, iterator::remove, "Remove operation should throw UnsupportedOperationException");
}

@Test
void testMultipleDuplicates() {
Bag<String> bag = new Bag<>();
bag.add("item1");
bag.add("item1");
bag.add("item1"); // Add three duplicates

assertEquals(3, bag.size(), "Bag size should be 3 after adding three duplicates");
assertTrue(bag.contains("item1"), "Bag should contain 'item1'");
}

@Test
void testLargeNumberOfElements() {
Bag<Integer> bag = new Bag<>();
for (int i = 0; i < 1000; i++) {
bag.add(i);
}
assertEquals(1000, bag.size(), "Bag should contain 1000 elements");
}

@Test
void testMixedTypeElements() {
Bag<Object> bag = new Bag<>();
bag.add("string");
bag.add(1);
bag.add(2.0);

assertTrue(bag.contains("string"), "Bag should contain a string");
assertTrue(bag.contains(1), "Bag should contain an integer");
assertTrue(bag.contains(2.0), "Bag should contain a double");
}

@Test
void testIteratorWithDuplicates() {
Bag<String> bag = new Bag<>();
bag.add("item1");
bag.add("item1");
bag.add("item2");

int count = 0;
for (String item : bag) {
assertTrue(item.equals("item1") || item.equals("item2"), "Item should be either 'item1' or 'item2'");
count++;
}
assertEquals(3, count, "Iterator should traverse all 3 items including duplicates");
}
}