Skip to content

TD2 — Collections & Exception Handling

← Part 1 Overview

S7 Inf A3 — Java for Graphical and Mobile Programming
Stéphane Derrode — Centrale Lyon
Part 1 — Java Fundamentals
Duration: 2h · Continue the td1 Maven project (or create td2)


Objectives

  • Use ArrayList and HashMap with the Car class from TD1
  • Write and throw custom exception classes
  • Persist a fleet to a text file with try-with-resources
  • Implement a stack-based RPN calculator

Part 1 — FleetManager with HashMap (40 min)

1.1 Custom exception

Create src/main/java/com/s7infa3/CarNotFoundException.java:

package com.s7infa3;

public class CarNotFoundException extends RuntimeException {
    private String owner;

    public CarNotFoundException(String owner) {
        super("No car registered for: " + owner);
        this.owner = owner;
    }

    public String getOwner() { return owner; }
}

1.2 Class FleetManager

Create src/main/java/com/s7infa3/FleetManager.java.

Method Description
register(String owner, Car c) Registers or replaces a car for the owner
lookup(String owner) Returns the car; throws CarNotFoundException if not found
unregister(String owner) Removes the entry; throws CarNotFoundException if not found
size() Returns the number of registered cars
listAll() Prints all entries sorted alphabetically by owner name
startAll() Calls start() on every registered car
saveToFile(String path) Saves as owner,model,power lines; declares throws IOException
loadFromFile(String path) Reloads from file (replaces existing data); declares throws IOException

Hints:

  • Use Map<String, Car> with HashMap.
  • For listAll(), sort the keys: List<String> keys = new ArrayList<>(map.keySet()); Collections.sort(keys);
  • For loadFromFile(), parse each line with line.split(",") (expect 3 parts) and create new Car(parts[1], Integer.parseInt(parts[2])).

1.3 Test in main()

FleetManager fm = new FleetManager();
fm.register("Alice",   new Car("Clio", 90));
fm.register("Bob",     new Car("Polo", 110));
fm.register("Charlie", new Car("Ferrari", 280));

System.out.println(fm.lookup("Alice")); // Car[Clio, 90hp, off, 0 km/h]
fm.listAll();   // Alice, Bob, Charlie (sorted)

fm.startAll();
System.out.println(fm.lookup("Bob"));   // Car[Polo, 110hp, on, 0 km/h]

fm.saveToFile("fleet.txt");

FleetManager fm2 = new FleetManager();
fm2.loadFromFile("fleet.txt");
System.out.println(fm2.size());         // 3

// Exception test
try {
    fm.lookup("Dave");
} catch (CarNotFoundException e) {
    System.out.println("Caught: " + e.getMessage());
    System.out.println("Owner: "  + e.getOwner());
}

Part 2 — Stack-based RPN Calculator (35 min)

Implement a Reverse Polish Notation (RPN) expression evaluator using Stack<Double>.

How RPN works

In RPN, operators come after their operands:

Infix expression RPN Result
3 + 4 3 4 + 7
(3 + 4) × 2 3 4 + 2 * 14
5 + 1×2 + 4×3 − 3 5 1 2 + 4 * + 3 - 14

Algorithm: scan tokens left to right: - Number → push it - Operator + - * /pop two values, compute, push result - At the end, the single remaining value is the result

2.1 Class RPNCalculator

Create src/main/java/com/s7infa3/RPNCalculator.java.

Method Description
evaluate(String expression) Evaluates a space-separated RPN string, returns double

Throw ArithmeticException("Division by zero") for / with zero divisor.
Throw IllegalArgumentException("Invalid expression") if the stack does not end with exactly one value.

Hint for parsing:

String[] tokens = expression.split("\\s+");
for (String token : tokens) {
    try {
        stack.push(Double.parseDouble(token)); // it's a number
    } catch (NumberFormatException e) {
        // it's an operator
    }
}

2.2 Test

RPNCalculator calc = new RPNCalculator();
System.out.println(calc.evaluate("3 4 +"));           // 7.0
System.out.println(calc.evaluate("3 4 + 2 *"));       // 14.0
System.out.println(calc.evaluate("5 1 2 + 4 * + 3 -")); // 14.0
System.out.println(calc.evaluate("10 2 /"));           // 5.0

try {
    calc.evaluate("10 0 /");
} catch (ArithmeticException e) {
    System.out.println("Caught: " + e.getMessage());   // Division by zero
}

Part 3 — Persistence Integration (15 min)

3.1 Calculator history

Add to RPNCalculator: - A List<String> field history storing each successfully evaluated expression - saveHistory(String path) throws IOException — writes one expression per line - loadHistory(String path) throws IOException — reads and re-evaluates each line, printing the result

3.2 Test

RPNCalculator calc = new RPNCalculator();
calc.evaluate("3 4 +");
calc.evaluate("10 2 /");
calc.saveHistory("history.txt");

RPNCalculator calc2 = new RPNCalculator();
calc2.loadHistory("history.txt");  // prints: 7.0 then 5.0

Part 4 — Exploration (remaining time)

4.1 Add boolean contains(String owner) and int countStarted() (counts cars where isStarted() is true) to FleetManager.

4.2 Make loadFromFile() robust: skip malformed lines (fewer than three comma-separated fields) instead of crashing.

4.3 Extend RPNCalculator to support % (modulo).

4.4 (Modern Java — optional) Replace the operator dispatch in evaluate() with a switch expression (Java 14+) instead of an if/else chain:

double b = stack.pop();
double a = stack.pop();
double result = switch (token) {
    case "+" -> a + b;
    case "-" -> a - b;
    case "*" -> a * b;
    case "/" -> {
        if (b == 0) throw new ArithmeticException("Division by zero");
        yield a / b;          // 'yield' returns the value of a block branch
    }
    default  -> throw new IllegalArgumentException("Unknown operator: " + token);
};
stack.push(result);

See the Python → Java reference for more Java 17 idioms.


Deliverable

No formal submission. Verify all main() outputs match expected values before leaving.


Common Errors

Error Cause Fix
NullPointerException on map.get() Key not present → returns null Use containsKey() or getOrDefault() first
EmptyStackException pop() on empty stack Guard with stack.size() >= 2 before popping
NumberFormatException not caught Token is neither number nor operator Catch it and handle as operator
FileNotFoundException Wrong path File path is relative to project root with Maven