快速的Java ACM IO模板
背景
对于大多数ACMer来说,Java I/O慢一直是个让人头疼的问题。由于Java的Scanner
的实现太过复杂(为了满足更多高级特性),使用它输入会比C/C++的scanf
慢好几倍,并且内存开销相当大。
目前网上最常见的快速模板是使用BufferedReader
,带缓冲并且按行读入测试数据,然后使用StringTokenizer
将每一行分片。但我找到的几个模板太过简单,虽然代码量少,却无法支持一些常用操作,比如:丢弃当前行,检索是否读取到了流尾,有一定局限性。 也有部分实现了这两个功能的模板,但一直没看到特别满意的,于是自己写了一个。
题外话: 还有很多对Java一知半解的人给出new Scanner(new BufferedInputStream())这种”减速”方法,Scanner的实现中有缓冲,慢并不是慢在这里。
简介
Reader
内部使用BufferedReader
的nextLine
方法从输入流读取数据,由内部方法innerNextLine
包装,同样使用StringTokenizer
分片。hasNext
方法会检查是否还有数据,它通过读入新数据并创建tokenizer来寻找下一个可用的token。因此,调用hasNext方法后,会确保调用tokenizer.nextToken()时可以得到数据,除非已经读取到输入流尾。nextLine
方法会读入一行新数据,同时舍弃tokenizer中的内容。next
方法会返回下一个数据。nextXXX
方法将next的数据包装成常见数据类型。
Writer
则是对BufferedWriter
的简单包装,提供了接受Object的print
和println
方法。注意,程序结束前一定要调用Writer的close
方法来刷新缓冲区并关闭输出流
性能测试
测试题目:给出未知行,每行第一个数N,随后给出N个数,求N个数之和。
测试输入:1000w行,均为5 1 10 100 1000 10000,结果为11111
测试环境:
Windows10 i7-7700HQ
Java: OpenJDK 12
C++: MinGW GCC
方法 | 执行时间 | 峰值内存 | 平均内存 |
Scanner | 91.2s | 88.7MB | 81.5MB |
Scanner(缓冲输入流包装 错误用法) | 106.9s | 96.2MB | 81.5MB |
Java快速IO模板 | 9.8s | 84.1MB | 79.1MB |
cin / cout | 107.5s | 0.4MB | 0.4MB |
scanf / printf | 10.1s | 0.4MB | 0.4MB |
C / C++输入输出挂 | 2.4s | 0.4MB | 0.4MB |
模板
class AReader { private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); private StringTokenizer tokenizer = new StringTokenizer(""); private String innerNextLine() { try { return reader.readLine(); } catch (IOException ex) { return null; } } public boolean hasNext() { while (!tokenizer.hasMoreTokens()) { String nextLine = innerNextLine(); if (nextLine == null) { return false; } tokenizer = new StringTokenizer(nextLine); } return true; } public String nextLine() { tokenizer = new StringTokenizer(""); return innerNextLine(); } public String next() { hasNext(); return tokenizer.nextToken(); } public int nextInt() { return Integer.parseInt(next()); } } class AWriter implements Closeable { private BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out)); public void print(Object object) throws IOException { writer.write(object.toString()); } public void println(Object object) throws IOException { writer.write(object.toString()); writer.write("\n"); } @Override public void close() throws IOException { writer.close(); } }
请问一下这个输出类是不是应该这么用呢
AWriter writer = new AWriter(System.out);
writer.println();
是的
博主请问你的读入类的String读入测试通过了吗,我测试好像不能用,如果你那边能用的话,能不能给个实例。