- Go to the repository link provided by your teacher
- Click the Fork button in the top right corner of the page
- This creates your own personal copy of the project under your GitHub account
- From your forked repository, click the green Code button
- Select the Codespaces tab
- Click Create codespace on main
- Wait for the Codespace to finish loading — this may take a minute
Once your Codespace is open you will need to configure JUnit for unit testing. Follow these steps:
- Open the Extensions panel on the left sidebar (or press
Ctrl+Shift+X) - Search for and install Extension Pack for Java by Microsoft if it is not already installed
- Open the Testing panel on the left sidebar
- Click Enable Java Tests and select JUnit Jupiter when prompted
- Maven will automatically download the JUnit dependencies — wait for it to finish
You should now see MovieDatabaseTest.java appear in the Testing panel. If everything is configured correctly you will be able to run the tests from there. Your file structure should look like this:
Movie.java
MovieDatabase.java
MovieDatabaseTest.java
movies_clean.csv
README.md
In this project, you will build a Movie Recommendation System in Java that reads real movie data from a CSV file and allows users to explore, search, sort, and receive recommendations based on genre and rating. The dataset contains 800 movies sourced from the MovieLens dataset, including each movie's title, release year, genres, average rating, and number of ratings.
This project covers the major topics from Unit 4:
| Topic | Where It Appears |
|---|---|
| 4.1 Ethical & Social Issues | Reflection questions |
| 4.2 Introduction to Data Sets | Understanding the CSV structure |
| 4.3–4.5 Arrays & Algorithms | Storing genre lists per movie |
| 4.6 Text Files | Reading movies_clean.csv with Scanner |
| 4.7 Wrapper Classes | Parsing rating and year strings to numbers |
| 4.8–4.10 ArrayList & Algorithms | Main movie collection and filtering |
| 4.11–4.13 2D Arrays | Genre-rating matrix |
| 4.14 Searching | Linear search by title |
| 4.15 Sorting | Sort by rating or number of ratings |
| 4.16–4.17 Recursion | Binary search and recursive filtering |
The file movies_clean.csv has the following columns:
title,year,genres,avgRating,numRatings
Forrest Gump,1994,Comedy/Drama/Romance/War,4.16,329
The Shawshank Redemption,1994,Crime/Drama,4.43,317
...
- title — Movie title (commas replaced with semicolons)
- year — Release year as a 4-digit string
- genres — Slash-separated list of genres (e.g.
Action/Sci-Fi/Thriller) - avgRating — Average user rating from 0.5 to 5.0
- numRatings — Number of users who rated this movie (minimum 20)
Create a class called Movie that represents a single movie from the dataset.
- Instance variables for
title(String),year(int),genres(String[]),avgRating(double), andnumRatings(int) - A constructor that accepts all five fields
- Getters for each instance variable
- A
toString()method that returns a readable summary, for example:The Shawshank Redemption (1994) | Crime/Drama | ★ 4.43 (317 ratings) - A
hasGenre(String genre)method that returnstrueif the movie belongs to the given genre (case-insensitive)
- Genres should be stored as a
String[]. Thesplit()method returns aString[]directly, so you do not need a loop to build the array — a single call tosplit("/")on the genres string is enough. - Use
String.join("/", genres)intoString()to print genres cleanly without brackets. - Use
equalsIgnoreCase()inhasGenre()so that"drama"and"Drama"both match.
Create a class called MovieDatabase that reads movies_clean.csv and stores all movies in an ArrayList<Movie>.
- Use the
FileandScannerclasses fromjava.ioandjava.util - The first line of the CSV is a header — skip it before your loop begins
- Use
hasNextLine()andnextLine()together to read through the file line by line - Split each line with
split(",")to get the individual fields, then usesplit("/")on the genres field to get aString[] - Use
Integer.parseInt()andDouble.parseDouble()to convert the year, rating, and numRatings fields - Add each parsed
Movieto anArrayList<Movie> - Add
throws IOExceptionto your method header - Print the total number of movies loaded when done
Loaded 800 movies.
Add the following methods to your MovieDatabase class. Read through all of them before you start writing — you may find that implementing some methods before others makes your life significantly easier.
Important: Since
moviesis astaticvariable andmainis astaticmethod, all methods inMovieDatabasethat accessmoviesmust also be declaredstatic.
public static void sortByRating()Sort the movies ArrayList in descending order by avgRating using selection sort or insertion sort. Do not use Collections.sort().
public static ArrayList<Movie> getTopRated(int n, String genre)Returns an ArrayList<Movie> containing the top n movies of the given genre by average rating. Use hasGenre() from your Movie class to check genre membership.
public static ArrayList<Movie> filterByGenre(String genre)Returns an ArrayList<Movie> containing all movies that belong to the given genre.
public static double getAverageRating()Returns the average avgRating across all movies in the dataset as a double.
public static ArrayList<Movie> getMostPopular(ArrayList<Movie> list, int n)Returns an ArrayList<Movie> containing the n movies with the highest numRatings from the given list.
public static Movie searchByTitle(String query)Returns the first Movie whose title contains the query string (case-insensitive) using a linear search. Return null if no match is found. Note that this should support partial matches — for example, searching "Die" should return "Die Hard".
public static int binarySearchByTitle(ArrayList<Movie> list, String title, int low, int high)Implement a binary search that returns the index of the movie with the matching title in the given list, or -1 if not found. The list must already be sorted alphabetically by title before calling this method. The binary search itself must be implemented recursively.
Hints: The following two lines are not on the AP Exam but are provided for your use:
Create a copy of an ArrayList so the original is not modified:
ArrayList<Movie> sorted = new ArrayList<Movie>(movies);Sort the copy alphabetically by title using a lambda expression:
sorted.sort((a, b) -> a.getTitle().compareTo(b.getTitle()));Sort once before your first recursive call, not inside the recursive method itself.
You will build a 2D array that summarizes movie counts broken down by genre and rating tier.
Define the following genres (in this order) as your rows:
String[] genres = {"Action", "Comedy", "Drama", "Horror", "Romance",
"Sci-Fi", "Thriller", "Animation", "Crime", "Documentary"};Define the following rating tiers as your columns:
| Index | Tier | Range |
|---|---|---|
| 0 | Poor | rating < 2.5 |
| 1 | Below Average | 2.5 ≤ rating < 3.0 |
| 2 | Average | 3.0 ≤ rating < 3.5 |
| 3 | Good | 3.5 ≤ rating < 4.0 |
| 4 | Excellent | rating ≥ 4.0 |
The matrix itself is just numbers — a clean int[][] with 10 rows (genres) and 5 columns (rating tiers):
int[][] matrix = new int[10][5];The genre and tier labels are defined separately as String[] arrays and are only used when printing:
String[] genres = {"Action", "Comedy", "Drama", "Horror", "Romance",
"Sci-Fi", "Thriller", "Animation", "Crime", "Documentary"};
String[] tiers = {"Poor", "Below Avg", "Average", "Good", "Excellent"};Create a method buildGenreMatrix() that returns an int[][] where each cell contains the count of movies belonging to that genre and rating tier. The rows represent genres and the columns represent rating tiers, so the matrix looks like this:
| Poor | Below Avg | Average | Good | Excellent | |
|---|---|---|---|---|---|
| Action | |||||
| Comedy | |||||
| Drama | |||||
| Horror | |||||
| Romance | |||||
| Sci-Fi | |||||
| Thriller | |||||
| Animation | |||||
| Crime | |||||
| Documentary |
For example, matrix[0][4] would contain the number of Action movies with an Excellent average rating (≥ 4.0). Note that a movie with multiple genres will be counted in multiple rows — a Crime/Drama film increments both matrix[8] (Crime) and matrix[2] (Drama).
Then create a method printGenreMatrix() that prints the matrix in a readable table format using the genre and tier label arrays.
The following helper method is provided for you — add it to MovieDatabase as-is:
private static int getRatingTier(double rating) {
if (rating < 2.5) return 0;
else if (rating < 3.0) return 1;
else if (rating < 3.5) return 2;
else if (rating < 4.0) return 3;
else return 4;
}Use the following outline as a starting point for buildGenreMatrix():
public static int[][] buildGenreMatrix() {
String[] genres = {"Action", "Comedy", "Drama", "Horror", "Romance",
"Sci-Fi", "Thriller", "Animation", "Crime", "Documentary"};
int[][] matrix = new int[10][5];
for (Movie m : movies) {
int col = getRatingTier(m.getAvgRating());
for (int i = 0; i < genres.length; i++) {
// your code here
}
}
return matrix;
}Add a method recommend(String genre, double minRating, int index) to MovieDatabase that uses recursion to traverse the movies ArrayList and return an ArrayList<Movie> of all movies matching the given genre with an average rating at or above minRating.
- The method must be recursive — no loops allowed
indexis the starting position for the current recursive call- The base case is when
indexreaches the end of the list - Each recursive call processes one movie and then recurses on the rest
ArrayList<Movie> results = db.recommend("Drama", 4.0, 0);Answer the following questions in a file called reflection.md. Write at least 3–5 sentences per question.
-
Implementation Order — Look back at the methods you wrote in Part 3. Did the order you implemented them matter? Were there any methods that became easier because you had already written another one? What would have been harder if you had tackled them in a different order?
-
Data Quality — The ratings in this dataset were collected from volunteer MovieLens users, not a random sample of the general population. How might this introduce bias into the data? What kinds of movies might be over- or underrepresented?
-
Privacy — The original dataset included a
userIdandtimestampfor every rating. What are the privacy risks of storing this kind of data? What could someone learn about a user from their rating history? -
Data Structure Choice — You used both an
ArrayListand a 2D array in this project. Explain why each was the right choice for its purpose. When would you switch from one to the other? -
Algorithm Choice — You implemented both linear search and binary search. Under what conditions is each one more appropriate? Use specific examples from this project to support your answer.
-
Class Design —
binarySearchByTitle()takes a list as a parameter rather than usingMovieDatabase.moviesdirectly, unlike most other methods in the class. Does this method really belong inMovieDatabase? Could you imagine a design where search and sort algorithms live in a separate class? What would be the advantages and disadvantages of that approach?
| Part | Points |
|---|---|
| Part 1 — Movie Class | 20 |
| Part 2 — Reading the CSV | 20 |
| Part 3 — MovieDatabase Methods | 40 |
| Part 4 — Genre-Rating Matrix | 10 |
| Part 5 — Reflection | 10 |
| Total | 100 |
| Extra Credit — Recursive Recommendation | +5 |
Submit the following files:
Movie.javaMovieDatabase.javareflection.md- Any additional classes you created
Do not submit movies_clean.csv — it is already in the repository.