Overview

I have been professionally working in Java for more than 7 years. But for solving various online judge problems (which I seldom do) I use C or C++. I realized I have never submitted a single problem in any online judge in Java. Today I will submit solution to a simple problem 10055 - Hashmat the Brave Warrior in Online Judge and try to understand the gotchas of Java.

Java Submission Specification

As stated here, for Online Judge following rule must be followed to submit solution in Java.

  1. Whole solution should be in a single Java file. File can have multiple non-public class(es)
  2. No package definition allowed
  3. Program must begin in public static main(String args[]) method of Main class
  4. Filename doesn’t matter but Main class can’t be public
// No package definition

// No class will be public
class Helper {
    // code
}

// Main.main() will be called by online judge
class Main {
    public static void main(String[] args) {
        // code
    }
}

Quick Summary

After successful submission I tweaked the code in various way to reduce the execution time. Following is the quick summary of execution time for JAVA 1.8.0 - OpenJDK Java which currently Online Judge uses.

Method/Class Used Execution Time
java.io.BufferedReader & String#split() 0.650s
java.io.BufferedReader & String#split() & try-with-resource 0.890s
java.io.BufferedReader & java.util.StringTokenizer 0.860s
java.util.Scanner 1.060s

Details

Following code is used to run the code.

// Filename: Oj10055.java
public class Oj10055 {
    public static void main(String[] args) throws IOException {
        Main.main(args);
    }
}

As Online Judge forces to code in Main class, only following code is submitted.

First I tried with java.util.Scanner as I expected this code to run fastest as this looks smartest but it is quite slow. It executes in 1.060s.

import java.util.*;

class Main {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        while (sc.hasNextLong()) {
            long a = sc.nextLong();
            long b = sc.nextLong();

            System.out.println(Math.abs(a - b));
        }

    }
}

I was quite disappointed as same code in ANSI C runs in 0.152s. So I tried to reduce the time a bit.

Then I used BufferedReader and parses data manually using String split(). This code run the fastest. Took around 0.650s.

import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        String line;
        while ((line = reader.readLine()) != null) {
            String[] parts = line.split(" ", 2);
            long a = Long.parseLong(parts[0]);
            long b = Long.parseLong(parts[1]);;

            System.out.println(Math.abs(a - b));
        }

        reader.close();
    }
}

Then I tried to make this code a bit smarter using try-with-resource but this slowed down the code and took 0.890s.

import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {

            String line;
            while ((line = reader.readLine()) != null) {
                String[] parts = line.split(" ", 2);
                long a = Long.parseLong(parts[0]);
                long b = Long.parseLong(parts[1]);;

                System.out.println(Math.abs(a - b));
            }

        }
    }
}

Then I updated code with java.util.StringTokenizer as its much easier to parse using this class. I used the following code but execution time increased. Took 0.860s. Probably java.util.StringTokenizer is not ideal for minor input parsing.

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

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        String line;
        StringTokenizer st;
        while ((line = reader.readLine()) != null) {
            st = new StringTokenizer(line);
            long a = Long.parseLong(st.nextToken());
            long b = Long.parseLong(st.nextToken());

            System.out.println(Math.abs(a - b));
        }

        reader.close();
    }
}

Bookmarks