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