Java Fast I/O for Competitive Programming
When solving Data Structures and Algorithms (DSA) problems in Java, beginners often face Time Limit Exceeded (TLE) errors even when their algorithm logic is perfectly optimal.
The most common culprit? Using java.util.Scanner to read large input streams and System.out.println to print outputs.
The Problem with Scanner
Scanner is heavily loaded with built-in regex parsing, locale checks, and a relatively small buffer size (1KB). When a problem requires reading hundreds of thousands of integers or strings, Scanner simply cannot process the stream fast enough, resulting in a TLE.
The Solution: BufferedReader + PrintWriter
To achieve maximum I/O performance in Java, competitive programmers use a combination of BufferedReader, StringTokenizer, and PrintWriter.
BufferedReader: Reads text from a character-input stream, buffering characters for efficient reading. It has a significantly larger default buffer size (8KB) and reads raw data without regex overhead.StringTokenizer: Breaks the input string into tokens much faster thanString.split()orScanner.nextInt().PrintWriter: Wraps the output stream to buffer print operations, drastically reducing the I/O overhead compared to callingSystem.out.printlnrepeatedly.
The FastReader Template
Below is a highly optimized, robust FastReader utility class. It includes safety checks for end-of-file streams and integrates PrintWriter for fast output.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.io.PrintWriter;
import java.io.BufferedOutputStream;
public class Main {
// FastReader Utility Class
static class FastReader {
BufferedReader br;
StringTokenizer st;
public FastReader() {
br = new BufferedReader(new InputStreamReader(System.in));
}
String next() {
while (st == null || !st.hasMoreElements()) {
try {
String line = br.readLine();
if (line == null) return null; // Handle end of stream
st = new StringTokenizer(line);
} catch (IOException e) {
e.printStackTrace();
}
}
return st.nextToken();
}
int nextInt() {
return Integer.parseInt(next());
}
long nextLong() {
return Long.parseLong(next());
}
double nextDouble() {
return Double.parseDouble(next());
}
String nextLine() {
String str = "";
try {
// Check for null tokenizer to prevent NullPointerException
if (st != null && st.hasMoreTokens()) {
str = st.nextToken("\n");
} else {
str = br.readLine();
}
} catch (IOException e) {
e.printStackTrace();
}
return str;
}
}
public static void main(String[] args) {
// Initialize FastReader and PrintWriter
FastReader sc = new FastReader();
PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out));
// Reading inputs
int n = sc.nextInt();
long k = sc.nextLong();
// Fast Output
out.println("Read inputs: " + n + ", " + k);
// CRITICAL: You must flush the output stream at the end
out.flush();
}
}
Big-O Performance Comparison
While both methods nominally process input linearly, the constant factors differ massively.
Scanner: Because of regex parsing, the time complexity behaves closer to where is the regex evaluation time per token. Processing integers takes ~3.0 seconds (High risk of TLE).FastReader: Operates in strict character-by-character reading time. Processing integers takes ~0.3 seconds (Safe execution).
Make it a habit to use this template for any DSA problem involving large arrays, trees, or graphs with heavy I/O constraints!