日拱一卒系列之(io性能對比)

1.引子

日常開發中,jdk類庫給我們提供了io操作相關api,有字節流,字符流;有負責讀的輸入流,負責寫的輸出流。且整個io相關的api非常豐富,豐富就意味着實際項目中有一定選擇上的困難,尤其對於剛入門的朋友(想想我們剛入門的時候,是不是被各種io類庫api繞暈)。

正好前幾天有朋友問,項目中有頻繁讀取和寫入文件的需求,如何選擇合適的io api?於是順手寫了一個benchmark的測試demo,特意分享出來,期望給有需要的朋友帶來一點參考。內容涉及寫入文件、讀取文件相關的io api對比

  • 寫入文件

    • FileOutputStream:字節輸出流

    • BufferedOutputStream:字節輸出流,提供緩衝區能力,減少系統調用,性能更好

    • FileWriter:字符輸出流

    • BufferedWriter:字符輸出流,提供緩衝區能力,減少系統調用,性能更好

  • 讀取文件

    • FileInputStream:字節輸入流

    • BufferedInputStream:字節輸入流,提供緩衝區能力,減少系統調用,性能更好

    • FileReader:字符輸入流

    • BufferedReader:字符輸入流,提供緩衝區能力,減少系統調用,性能更好

案例設計分別讀取寫入100m的文件,對比執行耗時,實現代碼比較簡單,下面讓我們一起來看一下。

2.案例

2.1.寫入文件

2.1.1.公共方法

/**
* 處理文件路徑,匹配Linux、Windows操作系統
* @param path
* @return
*/
private static String getRealFilePath(String path) {
  String FILE_SEPARATOR = System.getProperty("file.separator");
  return path.replace("/", FILE_SEPARATOR).replace("\\", FILE_SEPARATOR);
}

/**
* 根據文件名稱,創建File對象
* @param fileName
* @return
*/
private static File getFile(String fileName){
   String DIR = "D:\\tmp";
   String filePath = DIR + "\\" + fileName;
   filePath = getRealFilePath(filePath);

   log.info("寫入目錄:{},文件完整路徑:{}", DIR,filePath);
   File file = new File(filePath);
   if( file.exists()){
       file.delete();
   }

    return file;
}

2.1.2.FileOutputStream

/**
* 通過FileOutputStream寫入文件
*/
public static void writeFileByFileOutputStream() throws Exception{
        // 統計時間開始
        Long startTime = System.currentTimeMillis();
        log.info("通過FileOutputStream寫入文件開始");

        // 開始寫文件
        File file = getFile("one.txt");
        FileOutputStream out = new FileOutputStream(file);
        try {
            for(int i = 0; i < COUNT; i++){
                out.write(DATA.getBytes());
            }
        } finally {
            out.close();
        }

        // 統計時間結束
        Long endTime = System.currentTimeMillis();
        log.info("通過FileOutputStream寫入文件結束,寫入文件行數:{},共耗時:{}毫秒",COUNT,(endTime - startTime));
}

2.1.3.BufferedOutputStream

/**
* 通過BufferedOutputStream寫入文件
*/
public static void writeFileByBufferedOutputStream() throws Exception{
        // 統計時間開始
        Long startTime = System.currentTimeMillis();
        log.info("通過BufferedOutputStream寫入文件開始");

        // 開始寫文件
        File file = getFile("two.txt");
        FileOutputStream out = new FileOutputStream(file);
        BufferedOutputStream buffOut = new BufferedOutputStream(out);
        try {
            for(int i = 0; i < COUNT; i++){
                buffOut.write(DATA.getBytes());
            }
            buffOut.flush();
        } finally {
            buffOut.close();
            out.close();
        }

        Long endTime = System.currentTimeMillis();
        log.info("通過BufferedOutputStream寫入文件結束,寫入文件行數:{},共耗時:{}毫秒",COUNT,(endTime - startTime));
}

2.1.4.FileWriter

/**
* 通過FileWriter寫入文件
*/
public static void writeFileByFileWriter()throws Exception{
        // 統計時間開始
        Long startTime = System.currentTimeMillis();
        log.info("通過FileWriter寫入文件開始");

        // 開始寫文件
        File file = getFile("three.txt");
        FileWriter fw = new FileWriter(file);
        try {
            for(int i = 0; i < COUNT; i++){
                fw.write(DATA);
            }
        } finally {
            fw.close();
        }

        Long endTime = System.currentTimeMillis();
        log.info("通過FileWriter寫入文件結束,寫入文件行數:{},共耗時:{}毫秒",COUNT,(endTime - startTime));

}

2.1.5.BufferedWriter

/**
* 通過BufferedWriter寫入文件
*/
public static void writeFileByBufferedWriter() throws Exception{
        // 統計時間開始
        Long startTime = System.currentTimeMillis();
        log.info("通過BufferedWriter寫入文件開始");

        // 開始寫文件
        File file = getFile("four.txt");
        BufferedWriter bw = new BufferedWriter(new FileWriter(file));
        try {
            for(int i = 0; i < COUNT; i++){
                bw.write(DATA);
            }
            bw.flush();
        } finally {
            bw.close();
        }

        Long endTime = System.currentTimeMillis();
        log.info("通過BufferedWriter寫入文件結束,寫入文件行數:{},共耗時:{}毫秒",COUNT,(endTime - startTime));

}

2.1.6.執行測試

 /**
* main
* @param args
*/
public static void main(String[] args) throws Exception {
        // 1.FileOutputStream寫入文件
        writeFileByFileOutputStream();

        // 2.BufferedOutputStream寫入文件
        writeFileByBufferedOutputStream();

        // 3.FileWriter寫入文件
        writeFileByFileWriter();

        // 4.通過BufferedWriter寫入文件
        writeFileByBufferedWriter();

}

通過FileOutputStream寫入文件結束,寫入文件行數:1000000,共耗時:4765毫秒
通過BufferedOutputStream寫入文件結束,寫入文件行數:1000000,共耗時:297毫秒
通過FileWriter寫入文件結束,寫入文件行數:1000000,共耗時:296毫秒
通過BufferedWriter寫入文件結束,寫入文件行數:1000000,共耗時:250毫秒

從執行結果,我們看到,帶buffered緩衝區執行耗時更短,即性能更好,你知道爲什麼嗎?答案我想留給你去思考。

2.2.讀取文件

2.2.1.公共方法

/**
* 處理文件路徑,匹配Linux、Windows操作系統
* @param path
* @return
*/
private static String getRealFilePath(String path) {
    String FILE_SEPARATOR = System.getProperty("file.separator");
    return path.replace("/", FILE_SEPARATOR).replace("\\", FILE_SEPARATOR);
}

/**
* 根據文件名稱,創建File對象
* @param fileName
* @return
*/
private static File getFile(String fileName){
   String DIR = "D:\\tmp";
   String filePath = DIR + "\\" + fileName;
   filePath = getRealFilePath(filePath);

   log.info("讀取目錄:{},文件完整路徑:{}", DIR,filePath);
   File file = new File(filePath);

   return file;
}

2.2.2.FileInputStream

 /**
* 通過FileInputStream讀取文件
* @throws Exception
*/
public static void readFileByFileInputStream()throws  Exception{
        // 統計時間開始
        Long startTime = System.currentTimeMillis();
        log.info("通過FileInputStream讀取文件開始");

        // 開始讀文件
        File file = getFile("one.txt");
        FileInputStream in = new FileInputStream(file);
        byte[] bytes = new byte[1024];
        StringBuilder builder =  new StringBuilder();
        try {
            while(in.read(bytes) > 0){
                builder.append(new String(bytes,"UTF-8"));
            }

        } finally {
            in.close();
        }

        Long endTime = System.currentTimeMillis();
        log.info("通過FileInputStream讀取文件結束,共耗時:{}毫秒",(endTime - startTime));

}

2.2.3.BufferedInputStream

/**
* 通過BufferedInputStream讀取文件
* @throws Exception
*/
public static void readFileByBufferedInputStream() throws Exception{
        // 統計時間開始
        Long startTime = System.currentTimeMillis();
        log.info("通過BufferedInputStream讀取文件開始");

        // 開始讀文件
        File file = getFile("two.txt");
        FileInputStream in = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(in);
        byte[] bytes = new byte[1024];
        StringBuilder builder =  new StringBuilder();
        try {
            while(bis.read(bytes) > 0){
                builder.append(new String(bytes,"UTF-8"));
            }

        } finally {
            in.close();
        }

        Long endTime = System.currentTimeMillis();
        log.info("通過BufferedInputStream讀取文件結束,共耗時:{}毫秒",(endTime - startTime));
}

2.2.4.FileReader

/**
* 通過FileReader讀取文件
* @throws Exception
*/
public static void readFileByFileReader() throws Exception{
        // 統計時間開始
        Long startTime = System.currentTimeMillis();
        log.info("通過FileReader讀取文件開始");

        // 開始讀文件
        File file = getFile("three.txt");
        FileReader reader = new FileReader(file);
        char[] chars = new char[1024];
        StringBuilder builder =  new StringBuilder();
        try {
            while(reader.read(chars) > 0){
                builder.append(new String(chars));
            }

        } finally {
            reader.close();
        }

        Long endTime = System.currentTimeMillis();
        log.info("通過FileReader讀取文件結束,共耗時:{}毫秒",(endTime - startTime));
}

2.2.5.BufferedReader

/**
* 通過BufferedReader讀取文件
* @throws Exception
*/
public static void readFileByBufferedReader() throws Exception{
        // 統計時間開始
        Long startTime = System.currentTimeMillis();
        log.info("通過BufferedReader讀取文件開始");

        // 開始讀文件
        File file = getFile("four.txt");
        FileReader reader = new FileReader(file);
        BufferedReader bufferedReader = new BufferedReader(reader);
        StringBuilder builder =  new StringBuilder();
        try {
            String line = bufferedReader.readLine();
            while(line != null){
                builder.append(line);
                line = bufferedReader.readLine();
            }

        } finally {
            bufferedReader.close();
            reader.close();

        }

        Long endTime = System.currentTimeMillis();
        log.info("通過BufferedReader讀取文件結束,共耗時:{}毫秒",(endTime - startTime));
}

2.2.6.執行測試

/**
* main
* @param args
*/
public static void main(String[] args) throws Exception{
        // 1.FileInputStream讀取文件
        readFileByFileInputStream();

        // 2.BufferedInputStream讀取文件
        readFileByBufferedInputStream();

        // 3.FileReader讀取文件
        readFileByFileReader();

        // 4.BufferedReader讀取文件
        readFileByBufferedReader();

}

通過FileInputStream讀取文件結束,共耗時:766毫秒
通過BufferedInputStream讀取文件結束,共耗時:424毫秒
通過FileReader讀取文件結束,共耗時:688毫秒
通過BufferedReader讀取文件結束,共耗時:375毫秒

從執行結果,我們看到,帶buffered緩衝區執行耗時更短,即性能更好,你知道爲什麼嗎?答案我想留給你去思考。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章