Java Arena
Step 5 of 5

Putting it together

The last step puts the tools of the earlier steps into one program, the way module 29 did: a class for the data, a class for the logic, and a part that reads the input and prints the answers. This time the data class has an equals, and the logic class lets the list methods use it.

The tools

Tool What it does Taught in
an ArrayList of objects many objects, and loops that ask them questions module 19, Objects in a list
new Scanner(Paths.get(fileName)) in a try reads a file, line by line module 20, Reading files
null for "not found" an answer the caller checks module 22, Primitive and reference variables
contains, indexOf and remove(value) on a list of your own objects search the list with the class's equals module 23, Objects working together
a class that keeps a list as an instance variable the logic, apart from the input and output module 24, Lists inside objects
a loop that reads commands a text user interface module 25, Separating the UI from program logic

A worked example

The climbing gym of step 4 keeps its routes in a file, one per line: the wall, the color and the grade. Someone typed the red route on the north wall twice, the second time with another grade. A route is known by its wall and color, so the second line is the same route, and it must not end up in the list twice. At the front desk, climbers then log their ascents with the command climb and a line such as north wall,red.

ClimbingRoute is step 4's class, with its equals. Its getters are left out here, since this program doesn't use them.

The file routes.csv
north wall,red,5
north wall,blue,3
overhang,green,4
north wall,red,6
slab,yellow,2
Main.java
import java.nio.file.Paths;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Gym gym = readGym("routes.csv");
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("> ");
            String command = scanner.nextLine();
            if (command.equals("quit")) {
                System.out.println("Gym closed");
                break;
            }
            if (command.equals("climb")) {
                String[] parts = scanner.nextLine().split(",");
                ClimbingRoute route = gym.find(parts[0], parts[1]);
                if (route == null) {
                    System.out.println("No " + parts[1] + " route on the " + parts[0]);
                } else {
                    route.climb();
                    System.out.println(route);
                }
            } else if (command.equals("routes")) {
                for (ClimbingRoute route : gym.routes()) {
                    System.out.println(route);
                }
            } else {
                System.out.println("Unknown command: " + command);
            }
        }
    }

    public static Gym readGym(String fileName) {
        Gym gym = new Gym();
        try (Scanner scanner = new Scanner(Paths.get(fileName))) {
            while (scanner.hasNextLine()) {
                String[] parts = scanner.nextLine().split(",");
                ClimbingRoute route = new ClimbingRoute(parts[0], parts[1], Integer.valueOf(parts[2]));
                if (!gym.add(route)) {
                    System.out.println("Listed twice: " + route);
                }
            }
        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage());
        }
        return gym;
    }
}
Gym.java
import java.util.ArrayList;

public class Gym {
    private ArrayList<ClimbingRoute> routes;

    public Gym() {
        this.routes = new ArrayList<>();
    }

    public boolean add(ClimbingRoute route) {
        if (this.routes.contains(route)) {
            return false;
        }
        this.routes.add(route);
        return true;
    }

    public ClimbingRoute find(String wall, String color) {
        int index = this.routes.indexOf(new ClimbingRoute(wall, color, 0));
        if (index < 0) {
            return null;
        }
        return this.routes.get(index);
    }

    public ArrayList<ClimbingRoute> routes() {
        return this.routes;
    }
}
ClimbingRoute.java
public class ClimbingRoute {
    private String wall;
    private String color;
    private int grade;
    private int ascents;

    public ClimbingRoute(String wall, String color, int grade) {
        this.wall = wall;
        this.color = color;
        this.grade = grade;
        this.ascents = 0;
    }

    public void climb() {
        this.ascents++;
    }

    public boolean equals(Object compared) {
        if (this == compared) {
            return true;
        }
        if (!(compared instanceof ClimbingRoute)) {
            return false;
        }
        ClimbingRoute other = (ClimbingRoute) compared;
        return this.wall.equals(other.wall) && this.color.equals(other.color);
    }

    public String toString() {
        return this.color + " route on the " + this.wall + ", grade " + this.grade + ", ascents: " + this.ascents;
    }
}
Input
climb
north wall,red
climb
overhang,green
climb
north wall,red
climb
slab,blue
routes
quit
Output
Listed twice: red route on the north wall, grade 6, ascents: 0
> red route on the north wall, grade 5, ascents: 1
> green route on the overhang, grade 4, ascents: 1
> red route on the north wall, grade 5, ascents: 2
> No blue route on the slab
> red route on the north wall, grade 5, ascents: 2
blue route on the north wall, grade 3, ascents: 0
green route on the overhang, grade 4, ascents: 1
yellow route on the slab, grade 2, ascents: 0
> Gym closed
  • ClimbingRoute is the data, Gym is the logic, and main reads the file and the commands. Gym holds the list and answers with return values, false and null included, but prints nothing: main turns the answers into messages.
  • add asks contains first. contains compares with equals, so the fourth line of the file is found in the list, although it's a new object with another grade, and it's not added.
  • find needs a route to search with, so it makes one from the wall and the color. The grade doesn't matter to equals, so 0 will do. indexOf gives the index of the equal route in the list, and get gives that route itself, not a copy, so route.climb() in main changes the route in the list. The routes command shows the count.
  • contains and indexOf go through the list from the start, and compare each route with equals until one matches. A gym with a thousand routes can take a thousand comparisons for every search. Module 31's hash map finds a value by its key without going through the others.

Your task

Challenge 1 of 3

At the end of a woodworking course, each student's project is typed in, one per line, until an empty line: the student, the project and the hours it took, like Ada,bird box,6. Project.java is ready. In Main.java, read the projects into the ArrayList<Project>. The average isn't known until the last project has been read, so the list keeps them until then. After reading, print:

  • Projects: and the number of projects,
  • Average: , the average hours as a double (the hours added up, divided by the number of projects), and hours,
  • Longer than average:, and on the lines after it every project that took more hours than the average, in the order they were typed.

With no projects, print only No projects. For the input shown, the program prints:

Projects: 4
Average: 7.5 hours
Longer than average:
Ben: stool, 11 hours
Dev: bookshelf, 9 hours

Challenge 2

A climbing gym rents out climbing shoes, and has 2 pairs of one size on the shelf. This program reads its commands from a String, as module 20 showed a Scanner can. What does it print?

Challenge 3

The school play of step 4 keeps its cast in a Cast object. Role.java is step 4's class with its equals: two roles are equal when they're the same character in the same play, whoever plays it. Role has no getters for the play or the character, so a search for a role goes through equals. In Cast.java, replace the placeholders:

  • add(Role role) adds the role to the list and returns true. When an equal role is already in the list, it changes nothing and returns false.
  • recast(Role role) gives a role that's already in the list to another student: it puts the role it gets in the place of the equal role, at the same index, and returns true. When no equal role is in the list, it changes nothing and returns false.
  • studentFor(String play, String character) returns the student who plays that character in that play, or null when nobody does.

Use the list's contains and indexOf, which compare with Role's equals. Cast doesn't print anything. Main.java is a test drive, and it prints:

true
true
false
true
false
Theo null
[Ariel in The Tempest: Theo, Miranda in The Tempest: Liv]