Mastering the Java Collections Framework Hierarchy With Java Code and JUnit Testing

Mahad
10 min readNov 19, 2023

--

Java’s Collections Framework is a powerful set of classes and interfaces that provides a foundation for working with groups of objects. Whether you are dealing with lists, sets, maps, or queues, the Collections Framework offers a rich set of tools to manipulate and organize data efficiently. In this article, we will explore the key interfaces and classes in the Java Collections Framework hierarchy.

Collection Interface:

  • The root interface in the Collections Framework hierarchy.
  • Represents a group of objects known as elements.
  • Methods include add, remove, contains, size, etc.

StudentInformation Class (StudentInformation.java)

public class StudentInformation {
private String id;
private String name;

public StudentInformation(String id, String name) {
this.id = id;
this.name = name;
}

// Getters and setters

public String getId() {
return id;
}

public String getName() {
return name;
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
StudentInformation that = (StudentInformation) obj;
return id.equals(that.id);
}

@Override
public int hashCode() {
return id.hashCode();
}
}

CollectionExample Class (CollectionExample.java)

import java.util.Collection;
import java.util.ArrayList;

public class CollectionExample {

private Collection<StudentInformation> students;

public CollectionExample() {
students = new ArrayList<>();
}

public void addStudent(StudentInformation student) {
students.add(student);
}

public void removeStudent(StudentInformation student) {
students.remove(student);
}

public void printAllStudents() {
System.out.println("All Students:");
for (StudentInformation student : students) {
System.out.println("Student ID: " + student.getId() + ", Name: " + student.getName());
}
}

public int getTotalStudents() {
return students.size();
}

public boolean containsStudent(StudentInformation student) {
return students.contains(student);
}

public void clearAllStudents() {
students.clear();
}
}

JUnit Test Classes

AddStudentTest (AddStudentTest.java)

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class AddStudentTest {

@Test
public void testAddStudent() {
CollectionExample example = new CollectionExample();
StudentInformation student = new StudentInformation("1001", "Alice");

example.addStudent(student);
assertTrue(example.containsStudent(student));
}
}

RemoveStudentTest (RemoveStudentTest.java)

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class RemoveStudentTest {

@Test
public void testRemoveStudent() {
CollectionExample example = new CollectionExample();
StudentInformation student = new StudentInformation("1001", "Alice");

example.addStudent(student);
example.removeStudent(student);
assertFalse(example.containsStudent(student));
}
}

TotalStudentsTest (TotalStudentsTest.java)

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class TotalStudentsTest {

@Test
public void testTotalStudents() {
CollectionExample example = new CollectionExample();
StudentInformation student1 = new StudentInformation("1001", "Alice");
StudentInformation student2 = new StudentInformation("1002", "Bob");

example.addStudent(student1);
example.addStudent(student2);

assertEquals(2, example.getTotalStudents());
}
}

CollectionExampleTest (CollectionExampleTest.java)

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import java.util.Collection;
import java.util.ArrayList;

public class CollectionExampleTest {

@Test
public void testPrintAllStudents() {
CollectionExample example = new CollectionExample();
StudentInformation student1 = new StudentInformation("1001", "Alice");
StudentInformation student2 = new StudentInformation("1002", "Bob");

example.addStudent(student1);
example.addStudent(student2);

example.printAllStudents();
}
}

Set Interface:

  • Extends the Collection interface.
  • Represents an unordered collection of unique elements.
  • Common implementing classes include HashSet, TreeSet, and LinkedHashSet.

Below is a simple example of a StudentInformation class with rigorous JUnit tests using HashSet, TreeSet, and LinkedHashSet for storing student information.

StudentInformation Class

public class StudentInformation implements Comparable<StudentInformation> {
private String id;
private String name;

public StudentInformation(String id, String name) {
this.id = id;
this.name = name;
}

// Getters and setters

public String getId() {
return id;
}

public String getName() {
return name;
}

@Override
public int compareTo(StudentInformation other) {
return this.id.compareTo(other.id);
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
StudentInformation that = (StudentInformation) obj;
return id.equals(that.id);
}

@Override
public int hashCode() {
return id.hashCode();
}
}

Test Classes

HashSet Implementation Test

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

import java.util.Set;
import java.util.HashSet;

public class HashSetTest {

@Test
public void testHashSet() {
Set<StudentInformation> studentSet = new HashSet<>();

StudentInformation student1 = new StudentInformation("1001", "Alice");
StudentInformation student2 = new StudentInformation("1002", "Bob");

assertTrue(studentSet.add(student1));
assertTrue(studentSet.add(student2));
assertFalse(studentSet.add(student1)); // Duplicate element, should return false

assertEquals(2, studentSet.size());
}
}

TreeSet Implementation Test

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

import java.util.Set;
import java.util.TreeSet;

public class TreeSetTest {

@Test
public void testTreeSet() {
Set<StudentInformation> studentSet = new TreeSet<>();

StudentInformation student1 = new StudentInformation("1001", "Alice");
StudentInformation student2 = new StudentInformation("1002", "Bob");

assertTrue(studentSet.add(student1));
assertTrue(studentSet.add(student2));
assertFalse(studentSet.add(student1)); // Duplicate element, should return false

assertEquals(2, studentSet.size());
}
}

LinkedHashSet Implementation Test

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

import java.util.Set;
import java.util.LinkedHashSet;

public class LinkedHashSetTest {

@Test
public void testLinkedHashSet() {
Set<StudentInformation> studentSet = new LinkedHashSet<>();

StudentInformation student1 = new StudentInformation("1001", "Alice");
StudentInformation student2 = new StudentInformation("1002", "Bob");

assertTrue(studentSet.add(student1));
assertTrue(studentSet.add(student2));
assertFalse(studentSet.add(student1)); // Duplicate element, should return false

assertEquals(2, studentSet.size());
}
}

List Interface:

  • Extends the Collection interface.
  • Represents an ordered collection (sequence) that allows duplicate elements.
  • Common implementing classes include ArrayList, LinkedList, and Vector.

Below is a sample Java code that includes a Student class, a StudentManager class that uses the List interface with implementations using ArrayList, LinkedList, and Vector. It also includes JUnit tests for basic operations.

Student Class:

public class Student {
private String name;
private int age;

public Student(String name, int age) {
this.name = name;
this.age = age;
}

// Getters and setters

public String getName() {
return name;
}

public int getAge() {
return age;
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

StudentManager Class

import java.util.List;

public class StudentManager {
private List<Student> students;

public StudentManager(List<Student> students) {
this.students = students;
}

public void addStudent(Student student) {
students.add(student);
}

public void removeStudent(Student student) {
students.remove(student);
}

public List<Student> getAllStudents() {
return students;
}
}

Test Classes

ArrayList Implementation Test

import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;

public class ArrayListTest {

@Test
public void testArrayListImplementation() {
List<Student> students = new ArrayList<>();
StudentManager studentManager = new StudentManager(students);

// Test addStudent
studentManager.addStudent(new Student("John", 20));
studentManager.addStudent(new Student("Alice", 22));

// Test getAllStudents
List<Student> retrievedStudents = studentManager.getAllStudents();
Assert.assertEquals(2, retrievedStudents.size());

// Test removeStudent
studentManager.removeStudent(new Student("John", 20));
retrievedStudents = studentManager.getAllStudents();
Assert.assertEquals(1, retrievedStudents.size());
}
}

LinkedList Implementation Test

import org.junit.Assert;
import org.junit.Test;
import java.util.LinkedList;
import java.util.List;

public class LinkedListTest {

@Test
public void testLinkedListImplementation() {
List<Student> students = new LinkedList<>();
StudentManager studentManager = new StudentManager(students);

// Test addStudent
studentManager.addStudent(new Student("Bob", 21));
studentManager.addStudent(new Student("Eve", 23));

// Test getAllStudents
List<Student> retrievedStudents = studentManager.getAllStudents();
Assert.assertEquals(2, retrievedStudents.size());

// Test removeStudent
studentManager.removeStudent(new Student("Eve", 23));
retrievedStudents = studentManager.getAllStudents();
Assert.assertEquals(1, retrievedStudents.size());
}
}

Vector Implementation Test

import org.junit.Assert;
import org.junit.Test;
import java.util.List;
import java.util.Vector;

public class VectorTest {

@Test
public void testVectorImplementation() {
List<Student> students = new Vector<>();
StudentManager studentManager = new StudentManager(students);

// Test addStudent
studentManager.addStudent(new Student("Charlie", 19));
studentManager.addStudent(new Student("Diana", 24));

// Test getAllStudents
List<Student> retrievedStudents = studentManager.getAllStudents();
Assert.assertEquals(2, retrievedStudents.size());

// Test removeStudent
studentManager.removeStudent(new Student("Charlie", 19));
retrievedStudents = studentManager.getAllStudents();
Assert.assertEquals(1, retrievedStudents.size());
}
}

Queue Interface:

  • Extends the Collection interface.
  • Represents a collection designed for holding elements prior to processing.
  • Common implementing classes include LinkedList (can function as a queue) and PriorityQueue.

Below is an example of Java code with JUnit tests for managing student information using LinkedListand PriorityQueue.

Student Class:

public class Student {
private int id;
private String name;
private double gpa;

public Student(int id, String name, double gpa) {
this.id = id;
this.name = name;
this.gpa = gpa;
}

// Getters and setters

public int getId() {
return id;
}

public String getName() {
return name;
}

public double getGpa() {
return gpa;
}

// Override toString method for better representation
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", gpa=" + gpa +
'}';
}
}

StudentLinkedList Class:

import java.util.LinkedList;

public class StudentLinkedList {
private LinkedList<Student> studentList;

public StudentLinkedList() {
this.studentList = new LinkedList<>();
}

public void addStudent(Student student) {
studentList.add(student);
}

public Student removeStudent() {
if (isEmpty()) {
return null;
}
return studentList.remove();
}

public boolean isEmpty() {
return studentList.isEmpty();
}

public int getSize() {
return studentList.size();
}

// Additional methods as needed

public Student getStudent(int index) {
if (index < 0 || index >= getSize()) {
return null;
}
return studentList.get(index);
}
}

StudentPriorityQueue Class:

import java.util.PriorityQueue;

public class StudentPriorityQueue {
private PriorityQueue<Student> studentQueue;

public StudentPriorityQueue() {
this.studentQueue = new PriorityQueue<>(new StudentComparator()); // Assuming StudentComparator is implemented
}

public void addStudent(Student student) {
studentQueue.add(student);
}

public Student removeStudent() {
return studentQueue.poll();
}

public boolean isEmpty() {
return studentQueue.isEmpty();
}

public int getSize() {
return studentQueue.size();
}

// Additional methods as needed

public Student peek() {
return studentQueue.peek();
}
}

StudentComparator Class:

import java.util.Comparator;

public class StudentComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
// Compare students based on GPA
return Double.compare(s2.getGpa(), s1.getGpa()); // Descending order
}
}

JUnit Tests for StudentLinkedList:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class StudentLinkedListTest {

@Test
void testAddStudent() {
StudentLinkedList studentList = new StudentLinkedList();
Student student = new Student(1, "John Doe", 3.8);
studentList.addStudent(student);
assertEquals(1, studentList.getSize());
assertFalse(studentList.isEmpty());
}

@Test
void testRemoveStudent() {
StudentLinkedList studentList = new StudentLinkedList();
Student student = new Student(1, "Jane Doe", 4.0);
studentList.addStudent(student);
assertEquals(student, studentList.removeStudent());
assertEquals(0, studentList.getSize());
assertTrue(studentList.isEmpty());
}

// Additional tests as needed
}

JUnit Tests for StudentPriorityQueue:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class StudentPriorityQueueTest {

@Test
void testAddStudent() {
StudentPriorityQueue studentQueue = new StudentPriorityQueue();
Student student1 = new Student(1, "John Doe", 3.8);
Student student2 = new Student(2, "Jane Doe", 4.0);

studentQueue.addStudent(student1);
studentQueue.addStudent(student2);

assertEquals(2, studentQueue.getSize());
assertFalse(studentQueue.isEmpty());
assertEquals(student2, studentQueue.peek());
}

@Test
void testRemoveStudent() {
StudentPriorityQueue studentQueue = new StudentPriorityQueue();
Student student1 = new Student(1, "John Doe", 3.8);
Student student2 = new Student(2, "Jane Doe", 4.0);

studentQueue.addStudent(student1);
studentQueue.addStudent(student2);

assertEquals(student2, studentQueue.removeStudent());
assertEquals(1, studentQueue.getSize());
assertFalse(studentQueue.isEmpty());
}

// Additional tests as needed
}

Map Interface:

  • Not a sub interface of Collection.
  • Represents a collection of key-value pairs.
  • Common implementing classes include HashMap, TreeMap, and LinkedHashMap.

Below is an example Java code that includes implementations for HashMap, TreeMap, and LinkedHashMap for storing and managing student information. Additionally, there are JUnit tests for each implementation.

Student Class:

public class Student {

private String name;
private int id;

public Student(String name, int id) {
this.name = name;
this.id = id;
}

public String getName() {
return name;
}

public int getId() {
return id;
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
}

HashMapStudentInfo Class:

import java.util.Map;
import java.util.HashMap;

public class HashMapStudentInfo {

private Map<Integer, Student> studentMap;

public HashMapStudentInfo() {
this.studentMap = new HashMap<>();
}

public void addStudent(Student student) {
studentMap.put(student.getId(), student);
}

public Student getStudent(int studentId) {
return studentMap.get(studentId);
}
}

TreeMapStudentInfo Class:

import java.util.Map;
import java.util.TreeMap;

public class TreeMapStudentInfo {

private Map<Integer, Student> studentMap;

public TreeMapStudentInfo() {
this.studentMap = new TreeMap<>();
}

public void addStudent(Student student) {
studentMap.put(student.getId(), student);
}

public Student getStudent(int studentId) {
return studentMap.get(studentId);
}
}

LinkedHashMapStudentInfo Class:

import java.util.Map;
import java.util.LinkedHashMap;

public class LinkedHashMapStudentInfo {

private Map<Integer, Student> studentMap;

public LinkedHashMapStudentInfo() {
this.studentMap = new LinkedHashMap<>();
}

public void addStudent(Student student) {
studentMap.put(student.getId(), student);
}

public Student getStudent(int studentId) {
return studentMap.get(studentId);
}
}

JUnit Test Classes

HashMapStudentInfoTest

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class HashMapStudentInfoTest {

@Test
public void testAddAndGetStudent() {
HashMapStudentInfo studentInfo = new HashMapStudentInfo();
Student student1 = new Student("John Doe", 1);
studentInfo.addStudent(student1);

assertEquals(student1, studentInfo.getStudent(1));
}
}

TreeMapStudentInfoTest

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class TreeMapStudentInfoTest {

@Test
public void testAddAndGetStudent() {
TreeMapStudentInfo studentInfo = new TreeMapStudentInfo();
Student student1 = new Student("Jane Doe", 2);
studentInfo.addStudent(student1);

assertEquals(student1, studentInfo.getStudent(2));
}
}

LinkedHashMapStudentInfoTest

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class LinkedHashMapStudentInfoTest {

@Test
public void testAddAndGetStudent() {
LinkedHashMapStudentInfo studentInfo = new LinkedHashMapStudentInfo();
Student student1 = new Student("Alice Smith", 3);
studentInfo.addStudent(student1);

assertEquals(student1, studentInfo.getStudent(3));
}
}

Deque Interface:

  • Stands for “Double Ended Queue.”
  • Extends the Queue interface.
  • Represents a linear collection of elements where you can add and remove elements from both ends.
  • Common implementing classes include ArrayDeque and LinkedList.

Below is an example Java code for managing student information using the Deque interface with two implementing classes: ArrayDeque and LinkedList. Additionally, there are JUnit tests for each implementation.

Student Class:

public class Student {
private String name;
private int id;

public Student(String name, int id) {
this.name = name;
this.id = id;
}

// Getters and setters

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
}

StudentManager Class:

import java.util.Deque;

public class StudentManager {
private Deque<Student> studentDeque;

public StudentManager(Deque<Student> studentDeque) {
this.studentDeque = studentDeque;
}

public void addStudent(Student student) {
studentDeque.add(student);
}

public Student removeStudent() {
return studentDeque.poll();
}

public Student viewNextStudent() {
return studentDeque.peek();
}

public boolean isStudentQueueEmpty() {
return studentDeque.isEmpty();
}
}

ArrayDequeTest Class:

import org.junit.jupiter.api.Test;
import java.util.ArrayDeque;

import static org.junit.jupiter.api.Assertions.*;

class ArrayDequeTest {

@Test
void testArrayDequeOperations() {
ArrayDeque<Student> arrayDeque = new ArrayDeque<>();
StudentManager studentManager = new StudentManager(arrayDeque);

assertTrue(studentManager.isStudentQueueEmpty());

Student student1 = new Student("John Doe", 1);
studentManager.addStudent(student1);

assertFalse(studentManager.isStudentQueueEmpty());
assertEquals(student1, studentManager.viewNextStudent());

Student removedStudent = studentManager.removeStudent();
assertEquals(student1, removedStudent);

assertTrue(studentManager.isStudentQueueEmpty());
}
}

LinkedListTest Class:

import org.junit.jupiter.api.Test;
import java.util.LinkedList;

import static org.junit.jupiter.api.Assertions.*;

class LinkedListTest {

@Test
void testLinkedListOperations() {
LinkedList<Student> linkedList = new LinkedList<>();
StudentManager studentManager = new StudentManager(linkedList);

assertTrue(studentManager.isStudentQueueEmpty());

Student student1 = new Student("Jane Doe", 2);
studentManager.addStudent(student1);

assertFalse(studentManager.isStudentQueueEmpty());
assertEquals(student1, studentManager.viewNextStudent());

Student removedStudent = studentManager.removeStudent();
assertEquals(student1, removedStudent);

assertTrue(studentManager.isStudentQueueEmpty());
}
}

Below is a brief overview of some of the common classes that implement these interfaces:

ArrayList:

  • Implements the List interface.
  • Resizable-array implementation of the List interface.
  • Elements can be accessed directly by using the index.

LinkedList:

  • Implements the List and Deque interfaces.
  • Doubly-linked list implementation of the List and Deque interfaces.
  • Suitable for frequent insertions and removals.

HashSet:

  • Implements the Set interface.
  • Uses a hash table to store elements.
  • Does not guarantee the order of elements.

TreeSet:

  • Implements the SortedSet interface.
  • Uses a Red-Black tree to store elements.
  • Elements are sorted in natural order or according to a specified comparator.

HashMap:

  • Implements the Map interface.
  • Uses a hash table to store key-value pairs.
  • Does not guarantee the order of key-value pairs.

TreeMap:

  • Implements the SortedMap interface.
  • Uses a Red-Black tree to store key-value pairs.
  • Keys are sorted in natural order or according to a specified comparator.

Conclusion

Mastering the Java Collections Framework hierarchy is essential for effective Java programming. Choosing the right collection type based on your specific requirements is crucial for achieving the desired functionality, ordering, and performance. Whether you are working with sets, lists, maps, queues, or deques, the Java Collections Framework provides a versatile toolkit for handling collections of objects in a clean and efficient manner.

--

--