Using IO more efficiently in Java and C#

11 August 2020 • ☕️ 3 min read
#algorithms#java#CSharp

When you are doing problem solving(ps) in Java, This would be how you normally read text.

//
Scanner sc = new Scanner(System.in); 
sc.nextInt(); 
while(sc.next()){ 
	//... 
}

I wouldn't say it's a bad idea using Scanner and PrintStream for stdin/stdout but I have been wondering if I can make the code a little bit faster during stdin/stdout. I found a blog post about it (written in 🇰🇷). This suggests you can replace Scanner and PrintStream with BufferedReader and BufferedWriter. Once you grasp the idea how to use them, they will be your new buddies along the journey of problem solving.


Scanner vs BufferedReader

Buffer ✔

Scanner and BufferedReader each genereates the instance of 1024 chars and of 8192 chars. If you are reading large amount of string, it is better using BufferedReader. But you rarely encounters algorithms problems that require you to load large amount of string. This may not be the critical difference.

Convenience 😎

While BufferedReader can only read data, Scanner provides more parsing functionality like nextInt(). BufferedReader can only read string so if you want to get integer, you will need to parse it using Integer.parseInt(br.readLine()). Also, BufferedReader can consider new line as an end. If there is white space, you need to use StringTokenizer.

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));  //Decorating with InputStreamReader. Convert byte 스트림 into char using InputStreamReader
//Input is done per line. If there are whitespaces in one line, parsing is required. USe StringTokenizer.
StringTokenizer st = new StringTokenizer(br.readLine()); 

//One line
Integer.parseInt(st.nextToken()); 

//New line 
Integer.parseInt(br.readLine()); 

br.close();

Another difference is that Scanner does not require try-catch but BufferedReader has CheckedException. You need to catch IOException as mandatory.

Instead of try-catch, you can just throw exceptions like this:

public static void Main(string[] args) throw IOException
{
	//...
}

Speed 🌀🌀

BufferedReader use a technique called buffering and reduces the frequency of calling data from disk or stdin. Instead of reading string every time, it reads data in chunks at once and copy them into main memory (buffer). A user reads data from buffer when needed. This is a lot faster. This is the main reason using BufferedReader over Scanner.

The result using Scanner: Using Scanner

The result using BufferedReader Using BufferedReader

You can see BufferedReader takes less time.



PrintStream vs BufferedWriter

The comparison of these two are similar to the explanation above. Using BufferedWriter is faster but you need to do close(). flush() is not required as BufferedWriter flushes when the buffer is full. If needed, you can do it manually.

Examples

PrintStream

System.out.println("string");

BufferedWriter

int i = 0;
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
bw.write(String.valueOf(i));
bw.flush();
bw.close();


In CSharp

If you are a C# enthusiast, you can think of using StreamReader + BufferedStream instead of Console.ReadLine().

var reader = new StreamReader(new BufferedReader(Console.OpenStandardInput()))

In C#, using statement handle flusing/closing the stream. So you may want to write codes like this:

using(var reader = new StreamReader(new BufferedReader(Console.OpenStandardInput())))
using(var writer = new StreamWriter(new BufferedWriter(Console.OpenStandardOutput())))
{
	//...
}