Reading input
The Scanner line that vanishes, and BufferedReader beating it twelve times on 300,000 integers.
Reading input is where a first Java program meets the outside world, and it has one trap that catches nearly everybody and one performance cliff that matters the moment the input is large.
Scanner, and the line that vanishes
Scanner is the friendly one: it parses tokens for you.
Scanner s = new Scanner(System.in);
System.out.print("age? ");
int age = s.nextInt();
System.out.print("name? ");
String name = s.nextLine();Given the input 30 then Ana:
age? name? ### age = [30]
### name = []
### did it even wait for the second answer?The name is empty, the second prompt printed, and the program never waited. Ana was never read.
The cause is worth understanding rather than memorising a workaround for. nextInt() reads the digits 30 and stops there — it leaves the newline after them sitting in the buffer. nextLine() then reads "everything up to the next newline", finds one immediately, and returns the empty string between them.
So the rule: next, nextInt, nextDouble read tokens and leave the line ending; nextLine reads a line and consumes it. Mixing them is what breaks, and the two fixes are:
int age = s.nextInt();
s.nextLine(); // consume the rest of that line
String name = s.nextLine();
// or: read lines only, and parse yourself
int age = Integer.parseInt(s.nextLine().trim());The second is the one that scales, because it has one rule instead of two.
Two more edges worth knowing:
hasNextInt()beforenextInt(). Otherwise non-numeric input throwsInputMismatchException, and the offending token is not consumed — so a naive retry loop spins forever on the same bad token.- Locale.
nextDoubleuses the default locale, so3.14fails where the locale expects3,14. For anything parsing data rather than talking to a person,new Scanner(in).useLocale(Locale.ROOT).
BufferedReader, and what "faster" actually means
The same 300,000 integers, summed two ways:
### Scanner: 170 ms (sum 44999850000)
### BufferedReader: 14 ms (sum 44999850000)Twelve times. Same answer, and the difference is not a micro-optimisation — it is the difference between a batch job finishing and a competitive-programming submission timing out.
The reason is what each one does per call:
Scanneris a tokeniser built on regular expressions. EverynextInt()matches a pattern against the stream, and it synchronises. That machinery is what makes it pleasant for a prompt and expensive in a loop.BufferedReader.readLine()copies bytes into an array until it sees a line ending. No pattern, no parsing — you callInteger.parseIntyourself, which is a tight loop over digits.
try (BufferedReader r = new BufferedReader(new FileReader("big.txt"))) {
String line;
while ((line = r.readLine()) != null) {
sum += Integer.parseInt(line);
}
}Note the shape of that loop: assign inside the condition, compare to null. It looks odd the first time and it is the idiom, because readLine() returning null is the only way to learn the stream ended.
Which to reach for
| Situation | Use |
|---|---|
| a prompt, a handful of values | Scanner — the convenience is worth it |
| a file, or thousands of lines | BufferedReader |
| a whole small file at once | Files.readAllLines(path) |
| a large file, line by line, modern | Files.lines(path) — a stream, and close it |
| production input from a request | neither; the framework has parsed it already |
That last row matters more than it looks. In a Spring service you do not read System.in — the request body arrives parsed and validated, as the API course describes. Scanner and BufferedReader are for programs you run from a terminal: tools, imports, exercises, and the first hundred programs you write.
Closing, and the one you must not close
Both are resources. try-with-resources closes them:
try (BufferedReader r = new BufferedReader(new FileReader(path))) {
...
}The exception is System.in. Closing a Scanner wrapped around it closes the underlying stream, and it cannot be reopened — every later read in that process fails. In a program that reads once at the start, wrapping System.in in a try-with-resources is a bug that only shows up in the second thing that reads.