java 最快的input (ACM) Java高效IO

java 最快的input

问题: Scanner 非常非常非常慢

使用Scanner来输入非常方便,但很慢. 使用 BufferedReader 和 StringTokenizer 会更快, 但是在比赛中没有时间打这么繁杂的代码. 我们是否可以让java在ACM中

用起来更加简便呢?

 

java的输入比C繁杂太多了 (如果你使用BufferedReader会更糟糕)

 

 

JavaC/C++
 Scanner input =
    new Scanner(System.in);
 double x = input.nextDouble();
 
 double x;
 scanf("%lf",&x);
 

有多慢?

我测试从文件中读取 10,000,000个 int 或者 double. 用时时如下表. 我的运行环境是 Intel Core2 Duo 2.4GHz cpu, Windows XP Pro SP3, Sun JDK 6.0r22, 和GNU gcc 4.5.2.

表格1. 从文件中读取 10,000,000 int值用时

 

 

Input MethodTime (sec)
C scanf("%d", &arg) 3.78
Scanner.parseInt() 29.52
BufferedReader + inline Integer.parseInt 2.89
BufferedReader + Reader.nextInt method 3.01

表格2.从文件中读取 10,000,000 double值用时

Input MethodTime (sec)
C scanf("%lf", &arg) 11.9
Scanner.parseDouble() 66.86
BufferedReader + inline Double.parseDouble 3.06
BufferedReader + Reader.nextDouble method 3.14

 

 

列表1. 分别用Scanner 和 BufferedReader用以下示例代码来读取 。 我们分割输的一行为 string tokens,c因为一行可能包括

多个值。分割输入行来说, StringTokenizeris 比 string.split()快4倍以上.

 

 /** 用Scanner读取指定个数的整数*/
 static int scanInteger(int count) {
    Scanner scanner = new Scanner(input);
    int last = 0;
    while (count-- > 0) {
	    last = scanner.nextInt();
    }
    return last;
 }
 
 /** 使用BufferedReader读取指定个数的整数 */
 static int readIntegers(int count) 
        throws IOException {
    BufferedReader reader = new BufferedReader(
               new InputStreamReader(input) );
    StringTokenizer tokenizer = new StringTokenizer("");
    int last = 0;
    while (count-- > 0) {
        if (! tokenizer.hasMoreTokens() ) {
            tokenizer = new StringTokenizer(reader.readLine());
        }
        last = Integer.parseInt(tokenizer.nextToken());
    }
    return last;
 }
 

 

 

 

 

创建可重用代码

Java如何减轻读取输入的负担呢? Java和C/C++比较有如下优势: 更少语法错误, 也许更少逻辑错误, 更好的代码生成 和 在Eclipse中调试.

为了使java更容易使用, 让我们把输入代码放到一个单独的类吧. (你可以在源文件中放多个类, 前提是只有一个类是“public”的。) 我们可以复制粘贴 Reader 类到每一个ACM 编程任务中, 所以我们在竞赛中只要写一次。 当其他人在研究问题的时候,其中一个人就可以写好它。

列表2 只是其中一个例子. 如果你有更短,更有效的代码一定要告诉我. 我用了 StringTokenizer 代替 string.split() 因为StringTokenizer更快。 我使用了静态方法, 好处就是, 我们不必创建Reader的对象 , 并且使用默认访问权限(因为在同一个包下),所以就不用花时间打“public”。在我的测试中, 把输入方法放到一个单独的类中对速度基本没有影响 (有点惊讶)。但是对于余下的编程任务来说, 这代码的提高可重用性和简便性,。

好像还是挺长的, 但是你只要打一次,以后可以就复制粘贴。

所以在你的 ACM 编程任务中, 你可以写这样的代码:

     Reader.init( System.in );
     double x = Reader.nextDouble();
     int n = Reader.nextInt();

列表2. 读取int和double的可重用类。

/** 读取int和double的类 */
class Reader {
    static BufferedReader reader;
    static StringTokenizer tokenizer;

    /** 调用这个方法来初始化reader,即InputStream*/
    static void init(InputStream input) {
        reader = new BufferedReader(
                     new InputStreamReader(input) );
        tokenizer = new StringTokenizer("");
    }

    /** 获取下一段文本 */
    static String next() throws IOException {
        while ( ! tokenizer.hasMoreTokens() ) {
            //TODO add check for eof if necessary
            tokenizer = new StringTokenizer(
                   reader.readLine() );
        }
        return tokenizer.nextToken();
    }

    static int nextInt() throws IOException {
        return Integer.parseInt( next() );
    }
	
    static double nextDouble() throws IOException {
        return Double.parseDouble( next() );
    }
}

翻译by kucoder

posted @ 2015-11-17 22:55  苦码农  阅读(1454)  评论(0)    收藏  举报