Input-output for algorithms
Fast input and output matter when n reaches 10⁵ and above. This lesson compares Scanner and BufferedReader, shows StringTokenizer and a custom byte parser, buffered output, end-of-input handling and multiple test cases.
Learning goals
- Read large inputs quickly
- Write large outputs with a single flush
- Handle EOF and multi-test input correctly
Prerequisites
Scanner vs BufferedReader
Scanner is convenient (nextInt, nextLine) but slow because it parses with regular expressions. Reading 10⁶ numbers can take seconds. BufferedReader reads big chunks and returns whole lines as strings, and is several times faster.
Use Scanner for small inputs and learning; use BufferedReader or a custom reader when n is large.
1BufferedReader br = new BufferedReader(new InputStreamReader(System.in));2int n = Integer.parseInt(br.readLine().trim());3StringTokenizer st = new StringTokenizer(br.readLine());4int[] a = new int[n];5for (int i = 0; i < n; i++) a[i] = Integer.parseInt(st.nextToken());
StringTokenizer
StringTokenizer splits a line on whitespace without regular expressions. Combined with BufferedReader it is the usual fast reader. When tokens may continue onto the next line, refill the tokenizer lazily.
String.split(" ") is simpler but allocates an array per line and can be slower on very large input.
1static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));2static StringTokenizer st;3static String next() throws IOException {4while (st == null || !st.hasMoreTokens()) {5String line = br.readLine();6if (line == null) return null; // EOF7st = new StringTokenizer(line);8}9return st.nextToken();10}11static int nextInt() throws IOException { return Integer.parseInt(next()); }
Fast input parser
The fastest approach reads bytes from System.in into a buffer and parses numbers by hand, avoiding intermediate String objects. It handles negative numbers and skips whitespace. Use it when reading millions of integers.
Keep the buffer in a field and refill when exhausted.
1static final InputStream in = System.in;2static final byte[] buf = new byte[1 << 16];3static int len = 0, ptr = 0;4static int read() throws IOException {5if (ptr == len) { len = in.read(buf, 0, buf.length); ptr = 0; if (len <= 0) return -1; }6return buf[ptr++];7}8static int readInt() throws IOException {9int c = read();10while (c != '-' && (c < '0' || c > '9')) c = read();11boolean neg = false;12if (c == '-') { neg = true; c = read(); }13int x = 0;14while (c >= '0' && c <= '9') { x = x * 10 + (c - '0'); c = read(); }15return neg ? -x : x;16}
PrintWriter
System.out.println flushes frequently and is slow for large output. Wrap standard output in a PrintWriter over a BufferedWriter (or BufferedOutputStream), print as you go, and call flush() or close() once at the end.
Forgetting to flush loses the output.
1PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));2for (int i = 0; i < 5; i++) out.println(i * i);3out.flush();
BufferedWriter
BufferedWriter collects writes in memory and sends them in large blocks. It only writes strings and characters, so convert numbers with String.valueOf or Integer.toString, and write line breaks yourself.
Choose PrintWriter for convenience (println, printf) and BufferedWriter for the lowest overhead.
1BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));2for (int i = 0; i < 3; i++) { bw.write(Integer.toString(i)); bw.newLine(); }3bw.flush();
Standard input and output
Standard input (System.in), output (System.out) and error (System.err) are the three standard streams. Judges read stdin and compare stdout, so debug output must go to System.err or be removed.
In a terminal, redirect a file as input with java Main < input.txt and capture output with > out.txt.
1System.out.println("answer"); // graded2System.err.println("debug: n=" + 5); // ignored by the judge3// run: java Main < in.txt > out.txt
EOF
End of file means no more input. BufferedReader.readLine() returns null at EOF, Scanner.hasNext() returns false, and a byte read() returns -1. Use these to read “until the input ends” instead of counting.
Always handle a possible null line before calling methods on it.
1BufferedReader br = new BufferedReader(new InputStreamReader(System.in));2String line;3long total = 0;4while ((line = br.readLine()) != null) {5line = line.trim();6if (line.isEmpty()) continue;7total += Long.parseLong(line);8}9System.out.println(total);
Multiple test cases
Many problems start with the number of test cases t, followed by t independent inputs. Read t, then loop, resetting all per-case state inside the loop. Leaking state (a static counter, a reused array) from one case into the next is a common bug.
Collect all answers in one StringBuilder and print once at the end.
1int t = readInt();2StringBuilder sb = new StringBuilder();3while (t-- > 0) {4int n = readInt();5long sum = 0; // reset for every test case6for (int i = 0; i < n; i++) sum += readInt();7sb.append(sum).append('\n');8}9System.out.print(sb);
Practice ladder
Now try it yourself
Easy
- Read n then n integers with BufferedReader and print their sum
- Print the squares of 1 to 10 using one flush
Medium
- Read integers until EOF and print their maximum
- Write the multi-test skeleton for “read n and n numbers, print the sum”
Advanced
- Implement a byte-based reader that also parses longs and doubles and compare its speed with Scanner on 10⁶ numbers
Key takeaways
- Scanner is slow; use BufferedReader with StringTokenizer, or a byte reader, for large inputs.
- Buffer the output and flush once; debug prints go to System.err.
- Detect EOF with null or -1 and reset state per test case.