天天看點

Java基礎—Java檔案操作流處理效率對比

衆所周知,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();

    }

}

測試結果,如圖所示:

Java基礎—Java檔案操作流處理效率對比

從上圖可以看出,寫入行數超過20W以上時,FileOutputStream和NewOutputStream耗費時間遠遠超出其他4個類。為了清晰,讓我們放大其他4個類的圖:

Java基礎—Java檔案操作流處理效率對比

可以看出,這4個類中,BufferWriter和NewBufferedWriter所耗費時間更少,但總體差别不是很大。

讓我們再來看看,寫入26W行資料以下時的情況:

Java基礎—Java檔案操作流處理效率對比

可以看出,在資料量較小的情況下,這4個類所耗費時間的差異并不是很大,在更小的資料量下,它們的效率幾乎沒有差别。

後記

從以上分析可知(注意橫坐标寫入行數是指數級增加的),各個類的時間複雜度大緻為O(k),其中不同的類的k不同,導緻了最終巨大的差異。

這裡隻給出了測試結果,并未分析其底層實作原理,歡迎評論區留言。

另外,我沒有在其他機器測試,有興趣的小夥伴可以将自己的測試結果發出來,共同進步^_^。

附件

本次測試資料結果(若看不清,可以将浏覽器字型放大,或下載下傳到本地看)

Java基礎—Java檔案操作流處理效率對比

~~~~~~~~~~分割線~~~~~~~~~

評論區小夥伴“ andorxor”提出:

XXXOutputStream是用來寫二進制的,你把字元串轉換成位元組數組再寫自然就慢了,主要慢在轉換的過程。

是以,将程式修改,提前把字元和位元組内容都準備好,再次驗證。新程式如下:

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;//行數增長倍率

        String contentChars = "寫入檔案Data\n";//每行的内容(字元流)

        byte[] contentBytes = "寫入檔案Data\n".getBytes();//每行的内容(位元組流)

        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,contentBytes);

            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,contentBytes);

            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,contentChars);

            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,contentChars);

            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,contentBytes);

            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,contentChars);

            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, byte[] content) throws IOException {

        FileOutputStream fileOutputStream = new FileOutputStream(new File("./testFileOutputStream.txt"));

        while (--lineNum > 0) {

            fileOutputStream.write(content);

        }

        fileOutputStream.close();

    }

    public static void testBufferedOutputStream (int lineNum, byte[] content) throws IOException {

        FileOutputStream fileOutputStream = new FileOutputStream(new File("./testBufferedOutputStream.txt"));

        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);

        while (--lineNum > 0) {

            bufferedOutputStream.write(content);

        }

        bufferedOutputStream.close();

    }

    //面向字元

    public static void testFileWriter (int lineNum, String content) throws IOException {

        FileWriter fileWriter = new FileWriter("./testFileWriter.txt");

        while (--lineNum > 0) {

            fileWriter.write(content);

        }

        fileWriter.close();

    }

    public static void testBufferedWriter (int lineNum, String content) throws IOException {

        FileWriter fileWriter = new FileWriter("./testBufferedWriter.txt");

        BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

        while (--lineNum > 0) {

            bufferedWriter.write(content);

        }

        bufferedWriter.close();

    }

    /************************** NIO ****************************/

    public static void testNewOutputStream (int lineNum, byte[] content) throws IOException {

        OutputStream outputStream = Files.newOutputStream(Paths.get("./testNewOutputStream.txt"));

        while (--lineNum > 0) {

            outputStream.write(content);

        }

        outputStream.close();

    }

    public static void testNewBufferedWriter (int lineNum,String content) throws IOException {

        BufferedWriter newBufferedReader = Files.newBufferedWriter(Paths.get("./testNewBufferedWriter.txt"));

        while (--lineNum > 0) {

            newBufferedReader.write(content);

        }

        newBufferedReader.close();

    }

    public static void main (String[] args) throws IOException {

        //多次測試時可清空result.txt檔案

        FileWriter fileWriter = new FileWriter("./Result.txt");

        testDriver();

    }

}

結果如圖所示:

Java基礎—Java檔案操作流處理效率對比

可以看出和前面的案例幾乎沒有差異(圖就不畫了)。

是以XXXOutputStream效率低的原因并不是字元串轉換成位元組數組,而是其本身的實作方式所緻。

作者:xiaoxi666​