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緩衝區執行耗時更短,即性能更好,你知道爲什麼嗎?答案我想留給你去思考。