Before this lesson
Complete Java collections, exceptions and UTF-8 file I/O. Understand accumulation by key.
What you will be able to do
Combine validation, collections and stable sorting while preserving rejected row numbers.
Understand the mechanism
Define the input contract first: a course name and whole minutes separated by one semicolon, with no semicolons inside course names. This is a small project format, not a general CSV parser with quoting and escaping.
Separate parsing, validation, aggregation and presentation. split uses -1 to retain trailing empty fields. Keep original one-based line numbers for rejected rows instead of renumbering after filtering.
Only valid rows contribute to totals. Long values accumulate in a TreeMap for deterministic course-name order. Report rejected rows separately so partial success is visible. These ASCII names do not imply a Chinese collation policy.
Runnable example
Use JDK 21. Save Main.java in a separate practice directory, compile with javac -encoding UTF-8 Main.java, then run java Main. Each lesson is independent; do not combine multiple public Main classes.
import java.util.*;
public class Main {
public static void main(String[] args) {
String[] rows = {"Python;25", "SQL;20", "Python;35", "Java;bad", "SQL;-5", ";10"};
Map<String, Long> totals = new TreeMap<>();
List<Integer> rejected = new ArrayList<>();
for (int i = 0; i < rows.length; i++) {
String[] cells = rows[i].split(";", -1);
try {
if (cells.length != 2 || cells[0].isBlank()) throw new IllegalArgumentException();
int minutes = Integer.parseInt(cells[1].trim());
if (minutes < 0 || minutes > 240) throw new IllegalArgumentException();
totals.merge(cells[0].trim(), (long) minutes, Long::sum);
} catch (IllegalArgumentException e) { rejected.add(i + 1); }
}
totals.forEach((name, minutes) -> System.out.println(name + ": " + minutes));
System.out.println("Rejected lines: " + rejected);
}
}
Trace and verify
Python totals 60 and SQL totals 20. Rows 4, 5 and 6 have a malformed number, a negative duration and a missing name, producing [4, 5, 6].
Common mistakes and boundaries
Course names are case-sensitive: Python and python are different keys. Define a normalization policy before applying it during aggregation.
Practice challenge
Append Java;0 and Python;240 to check zero and the upper limit, then try SQL;241.
Reference solution and reasoning
Totals become Java 0, Python 300 and SQL 20; new row 9 is rejected. Do not discard a valid course merely because its duration is zero.