refactor: Bag data structure (#5340)

This commit is contained in:
Alex Klymenko 2024-08-17 21:31:29 +02:00 committed by GitHub
parent e8985b3edb
commit 404ad7272f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 153 additions and 60 deletions

View File

@ -4,23 +4,23 @@ import java.util.Iterator;
import java.util.NoSuchElementException; 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 Node<E> firstElement; // First element in the bag
private int size; // size of bag private int size; // Number of elements in the bag
private static final class Node<Element> { // Node class representing each element in the bag
private static final class Node<E> {
private Element content; private E content;
private Node<Element> nextElement; private Node<E> nextElement;
} }
/** /**
* Create an empty bag * Constructs an empty bag.
*/ */
public Bag() { public Bag() {
firstElement = null; firstElement = null;
@ -28,13 +28,17 @@ public class Bag<Element> implements Iterable<Element> {
} }
/** /**
* @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() { public boolean isEmpty() {
return firstElement == null; return size == 0;
} }
/** /**
* Returns the number of elements in the bag.
*
* @return the number of elements * @return the number of elements
*/ */
public int size() { public int size() {
@ -42,24 +46,26 @@ public class Bag<Element> implements Iterable<Element> {
} }
/** /**
* @param element - the element to add * Adds an element to the bag.
*
* @param element the element to add
*/ */
public void add(Element element) { public void add(E element) {
Node<Element> oldfirst = firstElement; Node<E> newNode = new Node<>();
firstElement = new Node<>(); newNode.content = element;
firstElement.content = element; newNode.nextElement = firstElement;
firstElement.nextElement = oldfirst; firstElement = newNode;
size++; 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 * @param element the element to check for
* @return true if bag contains element, otherwise false * @return true if the bag contains the element, false otherwise
*/ */
public boolean contains(Element element) { public boolean contains(E element) {
for (Element value : this) { for (E value : this) {
if (value.equals(element)) { if (value.equals(element)) {
return true; return true;
} }
@ -68,61 +74,37 @@ public class Bag<Element> implements Iterable<Element> {
} }
/** /**
* @return an iterator that iterates over the elements in this bag in * Returns an iterator over the elements in this bag.
* arbitrary order *
* @return an iterator that iterates over the elements in the bag
*/ */
public Iterator<Element> iterator() { @Override
public Iterator<E> iterator() {
return new ListIterator<>(firstElement); return new ListIterator<>(firstElement);
} }
@SuppressWarnings("hiding") // Private class for iterating over elements
private class ListIterator<Element> implements Iterator<Element> { private static class ListIterator<E> implements Iterator<E> {
private Node<Element> currentElement; private Node<E> currentElement;
ListIterator(Node<Element> firstElement) { ListIterator(Node<E> firstElement) {
currentElement = firstElement; this.currentElement = firstElement;
} }
@Override
public boolean hasNext() { public boolean hasNext() {
return currentElement != null; return currentElement != null;
} }
/**
* remove is not allowed in a bag
*/
@Override @Override
public void remove() { public E next() {
throw new UnsupportedOperationException();
}
public Element next() {
if (!hasNext()) { if (!hasNext()) {
throw new NoSuchElementException(); throw new NoSuchElementException();
} }
Element element = currentElement.content; E element = currentElement.content;
currentElement = currentElement.nextElement; currentElement = currentElement.nextElement;
return element; 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"));
}
} }

View File

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