前言
众所周知, Java 中有多种针对文件的操作类, 以面向字节流和字符流可分为两大类, 这里以写入为例:
面向字节流的: FileOutputStream 和 BufferedOutputStream
面向字符流的: FileWriter 和 BufferedWriter
近年来发展出 New I/O , 也叫 NIO, 里面又包装了两个类: NewOutputStream 和 NewBufferedWriter
现在, 我们建立测试程序, 比较这些类写入文件的性能.
机器配置
- Processor Name: Intel Core i7
- Processor Speed: 2.2 GHz
- Number of Processors: 1
- Total Number of Cores: 4
- L2 Cache (per Core): 256 KB
- L3 Cache: 6 MB
- Memory: 16 GB
测试程序
纵向比较: 几种文件操作类向文件中写入相同行数的内容 (每行内容均为 "写入文件 Data\n"), 比较其耗费时间
横向比较: 对于同一个文件操作类, 比较写入不同行数内容情况下所耗费时间; 本文以 2 的次方指数级增长行数
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.*;
- import java.nio.file.Files;
- import java.nio.file.Paths;
- public class testFileIO {
- public static void testDriver () throws IOException {
- int maxlineNum = 100000001;// 写入文件的最大行数
- int startlineNum = 1;// 写入文件的行数
- int Multiplying = 2;// 行数增长倍率
- long begin = 0L;
- long end = 0L;
- // 将时间统计写入文件 Result.txt 中
- FileWriter fileWriter = new FileWriter("./Result.txt", true);
- BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
- System.out.println("Test FileOutputStream begin.");
- for (int lineNum = startlineNum; lineNum <maxlineNum; lineNum *= Multiplying) {
- begin = System.currentTimeMillis();
- testFileOutputStream(lineNum);
- end = System.currentTimeMillis();
- long timeElapse_FileOutputStream = end - begin;
- bufferedWriter.write(String.valueOf(timeElapse_FileOutputStream)+"\t");
- }
- System.out.println("Test FileOutputStream end.\n");
- System.out.println("Test BufferedOutputStream begin.");
- bufferedWriter.write("\n");
- for (int lineNum = startlineNum; lineNum < maxlineNum; lineNum *= Multiplying) {
- begin = System.currentTimeMillis();
- testBufferedOutputStream(lineNum);
- end = System.currentTimeMillis();
- long timeElapse_BufferedOutputStream = end - begin;
- bufferedWriter.write(String.valueOf(timeElapse_BufferedOutputStream)+"\t");
- }
- System.out.println("Test BufferedOutputStream end.\n");
- System.out.println("Test FileWriter begin.");
- bufferedWriter.write("\n");
- for (int lineNum = startlineNum; lineNum < maxlineNum; lineNum *= Multiplying) {
- begin = System.currentTimeMillis();
- testFileWriter(lineNum);
- end = System.currentTimeMillis();
- long timeElapse_FileWriter = end - begin;
- bufferedWriter.write(String.valueOf(timeElapse_FileWriter)+"\t");
- }
- System.out.println("Test FileWriter end.\n");
- System.out.println("Test BufferedWriter begin.");
- bufferedWriter.write("\n");
- for (int lineNum = startlineNum; lineNum < maxlineNum; lineNum *= Multiplying) {
- begin = System.currentTimeMillis();
- testBufferedWriter(lineNum);
- end = System.currentTimeMillis();
- long timeElapse_BufferedWriter = end - begin;
- bufferedWriter.write(String.valueOf(timeElapse_BufferedWriter)+"\t");
- }
- System.out.println("Test BufferedWriter end.\n");
- System.out.println("Test NewOutputStream begin.");
- bufferedWriter.write("\n");
- for (int lineNum = startlineNum; lineNum < maxlineNum; lineNum *= Multiplying) {
- begin = System.currentTimeMillis();
- testNewOutputStream(lineNum);
- end = System.currentTimeMillis();
- long timeElapse_NewOutputStream = end - begin;
- bufferedWriter.write(String.valueOf(timeElapse_NewOutputStream)+"\t");
- }
- System.out.println("Test NewOutputStream end.\n");
- System.out.println("Test NewBufferedWriter begin.");
- bufferedWriter.write("\n");
- for (int lineNum = startlineNum; lineNum < maxlineNum; lineNum *= Multiplying) {
- begin = System.currentTimeMillis();
- testNewBufferedWriter(lineNum);
- end = System.currentTimeMillis();
- long timeElapse_NewBufferedWriter = end - begin;
- bufferedWriter.write(String.valueOf(timeElapse_NewBufferedWriter)+"\t");
- }
- System.out.println("Test NewOutputStream end.\n");
- bufferedWriter.close();
- }
- /************************** I/O *****************************/
- // 面向字节
- public static void testFileOutputStream (int lineNum) throws IOException {
- FileOutputStream fileOutputStream = new FileOutputStream(new File("./testFileOutputStream.txt"));
- while (--lineNum> 0) {
- fileOutputStream.write("写入文件 Data\n".getBytes());
- }
- fileOutputStream.close();
- }
- public static void testBufferedOutputStream (int lineNum) throws IOException {
- FileOutputStream fileOutputStream = new FileOutputStream(new File("./testBufferedOutputStream.txt"));
- BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
- while (--lineNum> 0) {
- bufferedOutputStream.write("写入文件 Data\n".getBytes());
- }
- bufferedOutputStream.close();
- }
- // 面向字符
- public static void testFileWriter (int lineNum) throws IOException {
- FileWriter fileWriter = new FileWriter("./testFileWriter.txt");
- while (--lineNum> 0) {
- fileWriter.write("写入文件 Data\n");
- }
- fileWriter.close();
- }
- public static void testBufferedWriter (int lineNum) throws IOException {
- FileWriter fileWriter = new FileWriter("./testBufferedWriter.txt");
- BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
- while (--lineNum> 0) {
- bufferedWriter.write("写入文件 Data\n");
- }
- bufferedWriter.close();
- }
- /************************** NIO ****************************/
- public static void testNewOutputStream (int lineNum) throws IOException {
- OutputStream outputStream = Files.newOutputStream(Paths.get("./testNewOutputStream.txt"));
- while (--lineNum> 0) {
- outputStream.write("写入文件 Data\n".getBytes());
- }
- outputStream.close();
- }
- public static void testNewBufferedWriter (int lineNum) throws IOException {
- BufferedWriter newBufferedReader = Files.newBufferedWriter(Paths.get("./testNewBufferedWriter.txt"));
- while (--lineNum> 0) {
- newBufferedReader.write("写入文件 Data\n");
- }
- newBufferedReader.close();
- }
- public static void main (String[] args) throws IOException {
- // 多次测试时可清空 result.txt 文件
- FileWriter fileWriter = new FileWriter("./Result.txt");
- testDriver();
- }
- }
测试结果
从上图可以看出, 写入行数超过 20W 以上时, FileOutputStream 和 NewOutputStream 耗费时间远远超出其他 4 个类. 为了清晰, 让我们放大其他 4 个类的图:
可以看出, 这 4 个类中, BufferWriter 和 NewBufferedWriter 所耗费时间更少, 但总体差别不是很大.
让我们再来看看, 写入 26W 行数据以下时的情况:
可以看出, 在数据量较小的情况下, 这 4 个类所耗费时间的差异并不是很大, 在更小的数据量下, 它们的效率几乎没有差别.
后记
从以上分析可知 (注意横坐标写入行数是指数级增加的), 各个类的时间复杂度大致为 O(1/kn), 其中不同的类的 k 不同, 导致了最终巨大的差异.
这里只给出了测试结果, 并未分析其底层实现原理, 欢迎评论区留言.
另外, 我没有在其他机器测试, 有兴趣的小伙伴可以将自己的测试结果发出来, 共同进步 ^_^
附件
本次测试数据结果 (若看不清, 可以下载到本地看)
来源: https://www.cnblogs.com/xiaoxi666/p/9531893.html