The full Java implementation, listed in the report as Appendix B. It is pulled directly from the repository’s source folder when the site is built.
15 filesMirrors /src
01Core data structures
Node.java// Singly linked list node · 11 lines
publicclassNode {Registration studData; // Field to store Registration dataNode next; // Field to store next Node reference// Task: Constructor to initialize node with RegistrationpublicNode(Registration studData) {this.studData = studData;this.next = null; }}
Registration.java// Record model · 66 lines
publicclassRegistration {privateString studentID;privateString courseID;privateint academicLevel; // Field to store class/groupprivateint regTime; // Field to store timeprivatedouble studDemandScore;// Constructor to initialize fieldspublicRegistration(String studentID, String courseID, int academicLevel, int regTime, double studDemandScore) {this.studentID = studentID;this.courseID = courseID;this.academicLevel = academicLevel;this.regTime = regTime;this.studDemandScore = 0;// temporary value prior to processing demand score (score hasn't been recorded// yet) }// Getters and setters for all fieldspublicStringgetStudentID() {return studentID; }publicvoidsetStudentID(String studentID) {this.studentID = studentID; }publicStringgetCourseID() {return courseID; }publicvoidsetCourseID(String courseID) {this.courseID = courseID; }publicintgetAcademicLevel() {return academicLevel; }publicvoidsetAcademicLevel(int academicLevel) {this.academicLevel = academicLevel; }publicintgetRegTime() {return regTime; }publicvoidsetRegTime(int regTime) {this.regTime = regTime; }publicdoublegetStudDemandScore() {return studDemandScore; }publicvoidsetStudDemandScore(double studDemandScore) {this.studDemandScore = studDemandScore; }// toString() method to format output line@OverridepublicStringtoString() {return studentID + ";" + courseID + ";" + academicLevel + ";" + regTime + ";" + Math.round(studDemandScore) + ";"; }// Math.round function used to round the demand score to the nearest whole// number}
LinkedList.java// Custom list with O(1) tail insert · 128 lines
publicclassLinkedList {// Task: Field to store head nodeNode head;// Task: Field to store size of listint size;// Task: (Optional) Field to store tail node for faster insertsNode tail;// Task: Method to insert node at headpublicvoidinsertNodeAtHead(Registration data) {Node newNode = newNode(data);if (head == null) { head = newNode; tail = newNode; size++;return; } newNode.next = head; head = newNode; size++; }// Task: Method to insert node at tailpublicvoidinsertNodeAtTail(Registration data) {Node newNode = newNode(data);//Case 1: handle special case (if node is empty)if (head == null) { head = newNode; tail = newNode; size++;return; } tail.next = newNode; tail = newNode; size++; }// Task: Method to delete node by valuepublicvoiddeleteNodeByValue(Registration data) {//Case 1: empty list if (head == null) {return; }//Case 2: if head node is to be deleted if (head.studData.equals(data)) { head = head.next; size--;return; }//Case 3: if any other node is to be deleted Node prev = head;Node curr = head.next;while (curr != null) {if (curr.studData.equals(data)) { prev.next = curr.next; size--;return; } prev = curr; curr = curr.next; } }// Task: Method to search for a nodepublicbooleansearchKey(Registration data) {Node temp = head;while (temp.next != null) {if (temp.studData.equals(data)) {returntrue; } temp = temp.next; }returnfalse; }// Task: Method to display/traverse all nodespublicvoiddisplayLinkedList() {Node temp = head;while (temp != null) {System.out.println(temp.studData); temp = temp.next; } }// Task: Method to convert list to array (Registration[])publicRegistration[] convToArray() {//count number of nodes int size = 0;Node temp = head;while (temp != null) { size++; temp = temp.next; }//create an array of that size Registration[] dataArray = newRegistration[size];//traverse through node to store into array temp = head;int index = 0;while (temp != null) { dataArray[index++] = temp.studData; temp = temp.next; }return dataArray; }}
import java.io.*;import java.util.*;publicclassInputValidator {privatestaticSet<String> validCourses = newHashSet<>(); // Empty list to store valid course codes extracted from// text file// Method to validate file path exists and is readable.publicstaticbooleanisValidFilePath(String filePath) {File file = newFile(filePath);return file.exists() && file.canRead(); }// Method to read and validate input file.publicstaticLinkedListreadAndValidateFile(String input) {LinkedList registrations = newLinkedList(); // Create instance of Layan's list (LinkedList class).// Check if file exists and is readable to avoid FileNotFoundException.if (!isValidFilePath(input)) {System.out.println("ERROR: File '" + input + "' not found or not readable. Please check that the file exists.");return registrations; // Stop the entire method }// If file is found, proceed to the BufferedReader section to actually read the// file.try (BufferedReader list = newBufferedReader(newFileReader(input))) { // BufferedReader to read the input file// more efficiently. It reads larger// chunks of data at a time, reducing// the number of I/O operations.String line;while ((line = list.readLine()) != null) { // readLine is like nextLine but for BufferedReader.String[] parts = line.split(";"); // Split line into parts based on semicolon.if (parts.length != 4) { // We are expecting exactly 4 fields per line.System.out.println("Invalid entry (wrong number of fields): " + line);continue; // Skip invalid entry }// Example line: 2021555;CS222;2;10;String studentID = parts[0];String courseID = parts[1];// We need to parse integers to convert from String.try {int academicLevel = Integer.parseInt(parts[2]);int studyTime = Integer.parseInt(parts[3]);// Validate non-empty string (for names, course titles)if (studentID.isEmpty() || courseID.isEmpty()) {System.out.println("Invalid string field in entry: " + line);continue; // Skip invalid entry }// Validate studentId.if (studentID.length() != 7 && studentID.length() != 5 ) { // Assuming student ID should be exactly 7 or 5 characters.System.out.println("Invalid student ID in entry: " + line);continue; // Skip invalid entry }// Validate credits/section as positive integers within range.if (academicLevel < 1 || academicLevel > 5) { // Student class should be 1-5.System.out.println("Invalid academic level in entry: " + line);continue; // Skip invalid entry }if (studyTime < 0 || studyTime > 23) { // Assuming time is in 24-hour formatSystem.out.println("Invalid study time in entry: " + line);continue; // Skip invalid entry }// If all validations pass, add courseID to validCourses Hashset. (Previously it// was empty). validCourses.add(courseID);// Create Registration object and add to linked list.Registration reg = newRegistration(studentID, courseID, academicLevel, studyTime, 0); registrations.insertNodeAtTail(reg);System.out.println("VALID: " + line); } catch (NumberFormatException e) {System.out.println("Invalid number format in entry: " + line);continue; // Skip invalid entry. } } } catch (IOException e) {System.out.println("Error reading file: " + e.getMessage()); }return registrations; }}
publicclassSelectionSortimplementsSorter {@Overridepublicvoidsort(Registration[] array) {// Sort in DESCENDING order (highest demand scores first).for (int i = 0; i < array.length - 1; i++) {int maxIndex = i; // Assume the current position has the highest score.for (int j = i + 1; j < array.length; j++) { // Look at all remaining elements to find the actual highest.if (array[j].getStudDemandScore() > array[maxIndex].getStudDemandScore()) { maxIndex = j; // Remember this as the new highest position. } }// SwapRegistration temp = array[i]; array[i] = array[maxIndex]; array[maxIndex] = temp; } }@OverridepublicStringname() {return"Selection Sort"; }}
InsertionSort.java// O(n²) · O(n) best · 26 lines
publicclassInsertionSortimplementsSorter {@Overridepublicvoidsort(Registration[] array) {// Start from the second element (index 1).for (int i = 1; i < array.length; i++) {Registration current = array[i];double currentScore = current.getStudDemandScore(); // Get the demand score of the current element for// comparison.int j = i - 1; // Start comparing with the element immediately to the left.// Shift elements that are smaller than current to the right.while (j >= 0 && array[j].getStudDemandScore() < currentScore) { array[j + 1] = array[j]; // Shift element to the right. j--; }// Insert current element in correct position. array[j + 1] = current; } }@OverridepublicStringname() {return"Insertion Sort"; }}
MergeSort.java// O(n log n) · 62 lines
publicclassMergeSortimplementsSorter {@Overridepublicvoidsort(Registration[] array) {if (array.length < 2) {return; // Base case: arrays of size 0 or 1 are already sorted }// Split the array into two halves.int mid = array.length / 2;Registration[] left = newRegistration[mid];Registration[] right = newRegistration[array.length - mid];// Copy first half of original array into left array.for (int i = 0; i < mid; i++) { left[i] = array[i]; // Copy elements from start to mid-1. }// Copy second half of original array into right array.for (int i = mid; i < array.length; i++) { right[i - mid] = array[i]; // Copy elements from mid to end.// i-mid converts the index (ex. if mid = 2, i = 2 becomes right[0]). }// Recursively sort both halves.sort(left);sort(right);// Merge the sorted halves back together.merge(array, left, right); }privatevoidmerge(Registration[] result, Registration[] left, Registration[] right) {// Three pointers:int i = 0; // Tracks position in left arrayint j = 0; // Tracks position in right arrayint k = 0; // Tracks position in result array// Compare elements from left and right arrays, take the larger one (descending// order).while (i < left.length && j < right.length) {if (left[i].getStudDemandScore() >= right[j].getStudDemandScore()) { result[k++] = left[i++]; } else { result[k++] = right[j++]; } }// Copy any remaining elements from left array.while (i < left.length) { result[k++] = left[i++]; }// Copy any remaining elements from right array.while (j < right.length) { result[k++] = right[j++]; } }@OverridepublicStringname() {return"Merge Sort"; }}
QuickSort.java// O(n log n) avg · 47 lines
publicclassQuickSortimplementsSorter {@Overridepublicvoidsort(Registration[] array) {quickSort(array, 0, array.length - 1); }privatevoidquickSort(Registration[] array, int low, int high) {if (low < high) {// Partition the array and get the pivot index.int pivotIndex = partition(array, low, high);// Recursively sort elements before and after pivot.quickSort(array, low, pivotIndex - 1);quickSort(array, pivotIndex + 1, high); } }privateintpartition(Registration[] array, int low, int high) {// Choose the rightmost element as pivot.double pivot = array[high].getStudDemandScore();int i = low - 1; // Index of smaller element.for (int j = low; j < high; j++) {// If current element is greater than or equal to pivot (descending order).if (array[j].getStudDemandScore() >= pivot) { i++;// Swap array[i] and array[j]Registration temp = array[i]; array[i] = array[j]; array[j] = temp; } }// Swap array[i+1] and array[high] (put pivot in correct position).Registration temp = array[i + 1]; array[i + 1] = array[high]; array[high] = temp;return i + 1; }@OverridepublicStringname() {return"Quick Sort"; }}
Comparators.java// Secondary sort keys · 56 lines
import java.util.Comparator;publicfinalclassComparators {// Private constructor to prevent instantiation (utility class)privateComparators() { }// Comparator to sort Registration by studentId (ascending)publicstaticfinalComparator<Registration> by_ID = newComparator<Registration>() {@Overridepublicintcompare(Registration r1, Registration r2) {return r1.getStudentID().compareTo(r2.getStudentID()); } };// Comparator to sort Registration by courseCode (A–Z)publicstaticfinalComparator<Registration> by_CourseCode = newComparator<Registration>() {@Overridepublicintcompare(Registration r1, Registration r2) {return r1.getCourseID().compareTo(r2.getCourseID()); } };// Comparator to sort Registration by academicLevel (ascending)publicstaticfinalComparator<Registration> by_Level = newComparator<Registration>() {@Overridepublicintcompare(Registration r1, Registration r2) {returnInteger.compare(r1.getAcademicLevel(), r2.getAcademicLevel()); } };// Comparator to sort Registration by regTime (ascending)publicstaticfinalComparator<Registration> by_RegTime = newComparator<Registration>() {@Overridepublicintcompare(Registration r1, Registration r2) {returnInteger.compare(r1.getRegTime(), r2.getRegTime()); } };// Static helper to get comparator by user choice (e.g., "id", "name",// "course")publicstaticComparator<Registration> getComparator(String criterion) {switch (criterion.toLowerCase()) { // Case insensitive matchingcase"id":return by_ID;case"course":return by_CourseCode;case"level":return by_Level;case"time":return by_RegTime;default:thrownewIllegalArgumentException("Unknown sorting criterion: " + criterion); } }}
05Benchmarking & entry point
Benchmarker.java// Times each sort · 53 lines
publicclassBenchmarker {// Helper method to make a copy of the arrayprivatestaticRegistration[] makeCopy(Registration[] original) {Registration[] copy = newRegistration[original.length]; // Create new empty arrayfor (int i = 0; i < original.length; i++) { // Loop through each element copy[i] = original[i]; // Copy each element }return copy; // Return the copy }// Runs all 4 sorting algorithms and times them.publicstaticvoidrunAllSorts(Registration[] originalArray) {System.out.println("--- RUNNING ALL SORTS ---");Timer timer = newTimer();// 1. SELECTION SORTRegistration[] copy1 = makeCopy(originalArray); // Make fresh copy. timer.start(); // Start timer.newSelectionSort().sort(copy1); // Do the sorting + create an object. timer.stop(); // Stop timer// File writing moved OUTSIDE timed section to measure only sorting timeFileService.writeRegistrationsToFile(copy1, "Sorted_Output_SS.txt"); // Save result.System.out.println("Selection Sort: " + timer.getElapsedTimeMillis() + " ms");// 2. INSERTION SORTRegistration[] copy2 = makeCopy(originalArray); // Fresh copy again. timer.start();newInsertionSort().sort(copy2); // Do the sorting + create an object. timer.stop();// File writing moved OUTSIDE timed section to measure only sorting timeFileService.writeRegistrationsToFile(copy2, "Sorted_Output_IS.txt"); // Save result.System.out.println("Insertion Sort: " + timer.getElapsedTimeMillis() + " ms");// 3. MERGE SORTRegistration[] copy3 = makeCopy(originalArray); // Fresh copy again. timer.start();newMergeSort().sort(copy3); // Do the sorting. timer.stop();// File writing moved OUTSIDE timed section to measure only sorting timeFileService.writeRegistrationsToFile(copy3, "Sorted_Output_MS.txt"); // Save result.System.out.println("Merge Sort: " + timer.getElapsedTimeMillis() + " ms");// 4. QUICK SORTRegistration[] copy4 = makeCopy(originalArray); // Fresh copy again. timer.start();newQuickSort().sort(copy4); // Do the sorting + create an object. timer.stop();// File writing moved OUTSIDE timed section to measure only sorting timeFileService.writeRegistrationsToFile(copy4, "Sorted_Output_QS.txt"); // Save result.System.out.println("Quick Sort: " + timer.getElapsedTimeMillis() + " ms"); }}
Timer.java// Millisecond stopwatch · 20 lines
publicclassTimer {// Private variables for safety, no other class can access these directly and effect the elapsed time.privatelong startTime;privatelong endTime;// Method to start timerpublicvoidstart() { startTime = System.currentTimeMillis(); // Simpler than nanoTime }// Method to stop timerpublicvoidstop() { endTime = System.currentTimeMillis(); }// Method to get elapsed time in millisecondspubliclonggetElapsedTimeMillis() {return endTime - startTime; }}
Main.java// Wires the pipeline together · 79 lines
import java.util.Arrays;publicclassMain {publicstaticvoidmain(String[] args) {Timer timer = newTimer();// Time reading operationSystem.out.println("Starting file reading..."); timer.start();LinkedList registrations = InputValidator.readAndValidateFile("Input.txt"); timer.stop(); // This timer times the file reading onlySystem.out.println();System.out.println("File reading completed in: " + timer.getElapsedTimeMillis() + " ms");System.out.println("Successfully read " + registrations.size + " registrations.");System.out.println();// Time processing operationSystem.out.println("Starting demand score calculations..."); timer.start();calculateAllDemandScores(registrations); timer.stop(); // This timer times the demand score calculations onlySystem.out.println("Demand score calculations completed in: " + timer.getElapsedTimeMillis() + " ms");System.out.println();// Convert linked list to arrayRegistration[] array = registrations.convToArray();// Time writing operationSystem.out.println("Writing unsorted data to file..."); timer.start();FileService.writeRegistrationsToFile(array, "Output.txt"); timer.stop(); // This timer times the file writing onlySystem.out.println("File writing completed in: " + timer.getElapsedTimeMillis() + " ms");System.out.println("Saved unsorted data to Output.txt");System.out.println();// Run and time all sorting algorithmsSystem.out.println("Starting sorting algorithms...");Benchmarker.runAllSorts(array);System.out.println("All sorting completed and results saved.");System.out.println();// Additional sorting using comparators (Not required but useful)System.out.println("\n--- ADDITIONAL COMPARATOR SORTING ---");// Sort by Student IDRegistration[] idSorted = registrations.convToArray();Arrays.sort(idSorted, Comparators.by_ID);FileService.writeRegistrationsToFile(idSorted, "Sorted_By_ID.txt");System.out.println("Saved student ID sorted data to Sorted_By_ID.txt");// Sort by Course CodeRegistration[] courseSorted = registrations.convToArray();Arrays.sort(courseSorted, Comparators.by_CourseCode);FileService.writeRegistrationsToFile(courseSorted, "Sorted_By_Course.txt");System.out.println("Saved course code sorted data to Sorted_By_Course.txt");// Sort by Academic LevelRegistration[] levelSorted = registrations.convToArray();Arrays.sort(levelSorted, Comparators.by_Level);FileService.writeRegistrationsToFile(levelSorted, "Sorted_By_Level.txt");System.out.println("Saved academic level sorted data to Sorted_By_Level.txt");// Sort by Registration TimeRegistration[] timeSorted = registrations.convToArray();Arrays.sort(timeSorted, Comparators.by_RegTime);FileService.writeRegistrationsToFile(timeSorted, "Sorted_By_Time.txt");System.out.println("Saved registration time sorted data to Sorted_By_Time.txt"); }// Calculate scores for all registrationspublicstaticvoidcalculateAllDemandScores(LinkedList registrations) {Node current = registrations.head;while (current != null) {DemandScorer.computeDemandScore(current.studData); current = current.next; } }}