Skip to main content

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 than String.split() or Scanner.nextInt().
  • PrintWriter: Wraps the output stream to buffer print operations, drastically reducing the I/O overhead compared to calling System.out.println repeatedly.

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 O(N×M)O(N \times M) where MM is the regex evaluation time per token. Processing 10510^5 integers takes ~3.0 seconds (High risk of TLE).
  • FastReader: Operates in strict O(N)O(N) character-by-character reading time. Processing 10510^5 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!