Skip to content

refactor: Bag data structure #5340

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 7 commits into from
Aug 17, 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
102 changes: 42 additions & 60 deletions src/main/java/com/thealgorithms/datastructures/bags/Bag.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,62 +4,68 @@
import java.util.NoSuchElementException;

/**
* Collection which does not allow removing elements (only collect and iterate)
* A collection that allows adding and iterating over elements but does not support element removal.
*
* @param <Element> - the generic type of an element in this bag
* @param <E> the type of elements in this bag
*/
public class Bag<Element> implements Iterable<Element> {
public class Bag<E> implements Iterable<E> {

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

private static final class Node<Element> {

private Element content;
private Node<Element> nextElement;
// Node class representing each element in the bag
private static final class Node<E> {
private E content;
private Node<E> nextElement;
}

/**
* Create an empty bag
* Constructs an empty bag.
*/
public Bag() {
firstElement = null;
size = 0;
}

/**
* @return true if this bag is empty, false otherwise
* Checks if the bag is empty.
*
* @return true if the bag is empty, false otherwise
*/
public boolean isEmpty() {
return firstElement == null;
return size == 0;
}

/**
* Returns the number of elements in the bag.
*
* @return the number of elements
*/
public int size() {
return size;
}

/**
* @param element - the element to add
* Adds an element to the bag.
*
* @param element the element to add
*/
public void add(Element element) {
Node<Element> oldfirst = firstElement;
firstElement = new Node<>();
firstElement.content = element;
firstElement.nextElement = oldfirst;
public void add(E element) {
Node<E> newNode = new Node<>();
newNode.content = element;
newNode.nextElement = firstElement;
firstElement = newNode;
size++;
}

/**
* Checks if the bag contains a specific element
* Checks if the bag contains a specific element.
*
* @param element which you want to look for
* @return true if bag contains element, otherwise false
* @param element the element to check for
* @return true if the bag contains the element, false otherwise
*/
public boolean contains(Element element) {
for (Element value : this) {
public boolean contains(E element) {
for (E value : this) {
if (value.equals(element)) {
return true;
}
Expand All @@ -68,61 +74,37 @@ public boolean contains(Element element) {
}

/**
* @return an iterator that iterates over the elements in this bag in
* arbitrary order
* Returns an iterator over the elements in this bag.
*
* @return an iterator that iterates over the elements in the bag
*/
public Iterator<Element> iterator() {
@Override
public Iterator<E> iterator() {
return new ListIterator<>(firstElement);
}

@SuppressWarnings("hiding")
private class ListIterator<Element> implements Iterator<Element> {
// Private class for iterating over elements
private static class ListIterator<E> implements Iterator<E> {

private Node<Element> currentElement;
private Node<E> currentElement;

ListIterator(Node<Element> firstElement) {
currentElement = firstElement;
ListIterator(Node<E> firstElement) {
this.currentElement = firstElement;
}

@Override
public boolean hasNext() {
return currentElement != null;
}

/**
* remove is not allowed in a bag
*/
@Override
public void remove() {
throw new UnsupportedOperationException();
}

public Element next() {
public E next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Element element = currentElement.content;
E element = currentElement.content;
currentElement = currentElement.nextElement;
return element;
}
}

/**
* main-method for testing
*/
public static void main(String[] args) {
Bag<String> bag = new Bag<>();

bag.add("1");
bag.add("1");
bag.add("2");

System.out.println("size of bag = " + bag.size());
for (String s : bag) {
System.out.println(s);
}

System.out.println(bag.contains(null));
System.out.println(bag.contains("1"));
System.out.println(bag.contains("3"));
}
}
111 changes: 111 additions & 0 deletions src/test/java/com/thealgorithms/datastructures/bag/BagTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.thealgorithms.datastructures.bag;

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 com.thealgorithms.datastructures.bags.Bag;
import java.util.Iterator;
import org.junit.jupiter.api.Test;

class BagTest {

@Test
void testBagOperations() {
Bag<String> bag = new Bag<>();
assertTrue(bag.isEmpty(), "Bag should be empty initially");
assertEquals(0, bag.size(), "Bag size should be 0 initially");

bag.add("item1");
bag.add("item2");
bag.add("item1"); // adding duplicate item

assertFalse(bag.isEmpty(), "Bag should not be empty after adding elements");
assertEquals(3, bag.size(), "Bag size should be 3 after adding 3 elements");

assertTrue(bag.contains("item1"), "Bag should contain 'item1'");
assertTrue(bag.contains("item2"), "Bag should contain 'item2'");
assertFalse(bag.contains("item3"), "Bag should not contain 'item3'");
assertFalse(bag.contains(null), "Bag should not contain null");

// Test iteration
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");
}

@Test
void testBagInitialization() {
Bag<String> bag = new Bag<>();
assertTrue(bag.isEmpty(), "Bag should be empty initially");
assertEquals(0, bag.size(), "Bag size should be 0 initially");
}

@Test
void testAddElements() {
Bag<String> bag = new Bag<>();
bag.add("item1");
bag.add("item2");
bag.add("item1"); // Adding duplicate item

assertFalse(bag.isEmpty(), "Bag should not be empty after adding elements");
assertEquals(3, bag.size(), "Bag size should be 3 after adding 3 elements");
}

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

assertTrue(bag.contains("item1"), "Bag should contain 'item1'");
assertTrue(bag.contains("item2"), "Bag should contain 'item2'");
assertFalse(bag.contains("item3"), "Bag should not contain 'item3'");
assertFalse(bag.contains(null), "Bag should not contain null");
}

@Test
void testContainsAfterRemoveOperation() {
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");
}

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

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

@Test
void testIteratorEmptyBag() {
Bag<String> bag = new Bag<>();
int count = 0;
for (String ignored : bag) {
org.junit.jupiter.api.Assertions.fail("Iterator should not return any items for an empty bag");
}
assertEquals(0, count, "Iterator should not traverse any items in an empty bag");
}

@Test
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");
}
}