What's the fastest way to read from System.in in Java?

JavaOptimizationInputstreamStdin

Java Problem Overview


I am reading bunch of integers separated by space or newlines from the standard in using Scanner(System.in).

Is there any faster way of doing this in Java?

Java Solutions


Solution 1 - Java

> Is there any faster way of doing this in Java?

Yes. Scanner is fairly slow (at least according to my experience).

If you don't need to validate the input, I suggest you just wrap the stream in a BufferedInputStream and use something like String.split / Integer.parseInt.


A small comparison:

Reading 17 megabytes (4233600 numbers) using this code

Scanner scanner = new Scanner(System.in);
while (scanner.hasNext())
    sum += scanner.nextInt();

took on my machine 3.3 seconds. while this snippet

BufferedReader bi = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = bi.readLine()) != null)
    for (String numStr: line.split("\\s"))
        sum += Integer.parseInt(numStr);

took 0.7 seconds.

By messing up the code further (iterating over line with String.indexOf / String.substring) you can get it down to about 0.1 seconds quite easily, but I think I've answered your question and I don't want to turn this into some code golf.

Solution 2 - Java

I created a small InputReader class which works just like Java's Scanner but outperforms it in speed by many magnitudes, in fact, it outperforms the BufferedReader as well. Here is a bar graph which shows the performance of the InputReader class I have created reading different types of data from standard input:

enter image description here

Here are two different ways of finding the sum of all the numbers coming from System.in using the InputReader class:

int sum = 0;
InputReader in = new InputReader(System.in);

// Approach #1
try {

    // Read all strings and then parse them to integers (this is much slower than the next method).
    String strNum = null;
    while( (strNum = in.nextString()) != null )
        sum += Integer.parseInt(strNum);

} catch (IOException e) { }

// Approach #2
try {

    // Read all the integers in the stream and stop once an IOException is thrown
    while( true ) sum += in.nextInt();

} catch (IOException e) { }

Solution 3 - Java

If you asking from competitive programming point of view, where if the submission is not fast enough, it will be TLE.
Then you can check the following method to retrieve String from System.in. I have taken from one of the best coder in java(competitive sites)

private String ns()
{
	int b = skip();
	StringBuilder sb = new StringBuilder();
	while(!(isSpaceChar(b))){ // when nextLine, (isSpaceChar(b) && b != ' ')
		sb.appendCodePoint(b);
		b = readByte();
	}
	return sb.toString();
}`

Solution 4 - Java

You can read from System.in in a digit by digit way. Look at this answer: https://stackoverflow.com/a/2698772/3307066.

I copy the code here (barely modified). Basically, it reads integers, separated by anything that is not a digit. (Credits to the original author.)

private static int readInt() throws IOException {
    int ret = 0;
    boolean dig = false;
    for (int c = 0; (c = System.in.read()) != -1; ) {
        if (c >= '0' && c <= '9') {
            dig = true;
            ret = ret * 10 + c - '0';
        } else if (dig) break;
    }
    return ret;
}

In my problem, this code was approx. 2 times faster than using StringTokenizer, which was already faster than String.split(" "). (The problem involved reading 1 million integers of up to 1 million each.)

Solution 5 - Java

StringTokenizer is a much faster way of reading string input separated by tokens.

Check below example to read a string of integers separated by space and store in arraylist,

String str = input.readLine(); //read string of integers using BufferedReader e.g. "1 2 3 4"
List<Integer> list = new ArrayList<>();
StringTokenizer st = new StringTokenizer(str, " ");
while (st.hasMoreTokens()) {
	list.add(Integer.parseInt(st.nextToken()));
} 

Solution 6 - Java

In programming perspective this customized Scan and Print class is way better than Java inbuilt Scanner and BufferedReader classes.

import java.io.InputStream;
import java.util.InputMismatchException;
import java.io.IOException;

public class Scan
{

private byte[] buf = new byte[1024];

private int total;
private int index;
private InputStream in;

public Scan()
{
	in = System.in;
}

public int scan() throws IOException
{
	
	if(total < 0)
		throw new InputMismatchException();
	
	if(index >= total)
	{
		index = 0;
		total = in.read(buf);
		if(total <= 0)
			return -1;
	}
	
	return buf[index++];
}


public int scanInt() throws IOException
{
	
	int integer = 0;
	
	int n = scan();
	
	while(isWhiteSpace(n))   /*  remove starting white spaces   */
		n = scan();
	
	int neg = 1;
	if(n == '-')
	{
		neg = -1;
		n = scan();
	}
	
	while(!isWhiteSpace(n))
	{
		
		if(n >= '0' && n <= '9')
		{
			integer *= 10;
			integer += n-'0';
			n = scan();
		}
		else
			throw new InputMismatchException();
	}
	
	return neg*integer;
}


public String scanString()throws IOException
{
	StringBuilder sb = new StringBuilder();
	
	int n = scan();
	
	while(isWhiteSpace(n))
		n = scan();
	
	while(!isWhiteSpace(n))
	{
		sb.append((char)n);
		n = scan();
	}
	
	return sb.toString();
}


public double scanDouble()throws IOException
{
	double doub=0;
	int n=scan();
	while(isWhiteSpace(n))
	n=scan();
	int neg=1;
	if(n=='-')
	{
		neg=-1;
		n=scan();
	}
	while(!isWhiteSpace(n)&& n != '.')
	{
		if(n>='0'&&n<='9')
		{
			doub*=10;
			doub+=n-'0';
			n=scan();
		}
		else throw new InputMismatchException();
	}
	if(n=='.')
	{
		n=scan();
		double temp=1;
		while(!isWhiteSpace(n))
		{
			if(n>='0'&&n<='9')
			{
				temp/=10;
				doub+=(n-'0')*temp;
				n=scan();
			}
			else throw new InputMismatchException();
		}
	}
	return doub*neg;
}

public boolean isWhiteSpace(int n)
{
	if(n == ' ' || n == '\n' || n == '\r' || n == '\t' || n == -1)
		return true;
	
	return false;
}

public void close()throws IOException
{
	in.close();
}
}

And the customized Print class can be as follows

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;

public class Print
{
private BufferedWriter bw;

public Print()
{
	this.bw = new BufferedWriter(new OutputStreamWriter(System.out));
}


public void print(Object object)throws IOException
{
	bw.append("" + object);
}

public void println(Object object)throws IOException
{
	print(object);
	bw.append("\n");
}


public void close()throws IOException
{
	bw.close();
}

}

Solution 7 - Java

> You can use BufferedReader for reading data

BufferedReader inp = new BufferedReader(new InputStreamReader(System.in));
  int t = Integer.parseInt(inp.readLine());
  while(t-->0){
    int n = Integer.parseInt(inp.readLine());
    int[] arr = new int[n];
    String line = inp.readLine();
    String[] str = line.trim().split("\\s+");
 	for(int i=0;i<n;i++){
	  arr[i] = Integer.parseInt(str[i]);
	}

> And for printing use StringBuffer

    StringBuffer sb = new StringBuffer();
    for(int i=0;i<n;i++){
        	  sb.append(arr[i]+" "); 
        	}
    System.out.println(sb);

Solution 8 - Java

Here is the full version fast reader and writer. I also used Buffering.

import java.io.*;
import java.util.*;


public class FastReader {
	private static StringTokenizer st;
	private static BufferedReader in;
	private static PrintWriter pw;
	
	
	public static void main(String[] args) throws IOException {

		in = new BufferedReader(new InputStreamReader(System.in));
		pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
		st = new StringTokenizer("");
	
		pw.close();
	}

	private static int nextInt() throws IOException {
		return Integer.parseInt(next());
	}
	private static long nextLong() throws IOException {
		return Long.parseLong(next());
	}
	private static double nextDouble() throws IOException {
		return Double.parseDouble(next());
	}
	private static String next() throws IOException {
		while(!st.hasMoreElements() || st == null){
			st = new StringTokenizer(in.readLine());
		}
		return st.nextToken();
	}
}

Solution 9 - Java

Reading from disk, again and again, makes the Scanner slow. I like to use the combination of BufferedReader and Scanner to get the best of both worlds. i.e. speed of BufferredReader and rich and easy API of the scanner.

Scanner scanner = new Scanner(new BufferedReader(new InputStreamReader(System.in)));

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionpathikritView Question on Stackoverflow
Solution 1 - JavaaioobeView Answer on Stackoverflow
Solution 2 - Javawill.fisetView Answer on Stackoverflow
Solution 3 - JavaLokiView Answer on Stackoverflow
Solution 4 - JavajbarramedaView Answer on Stackoverflow
Solution 5 - JavaHetal RachhView Answer on Stackoverflow
Solution 6 - JavaPaul92View Answer on Stackoverflow
Solution 7 - JavaShywalkerView Answer on Stackoverflow
Solution 8 - Javamr.sanatbekView Answer on Stackoverflow
Solution 9 - JavaHeisenbergView Answer on Stackoverflow