为了比较 Java 几种方式读取字符的速度,博主从网上下载了一个 三国演义.txt ,文件大小 1196684 字节,即 1.1 MB。
先放总结
如果是对图片、音乐、视频、压缩包等文件的拷贝,推荐使用 缓冲字节输入/输出流(BufferedOutputStream)+字节数组 (第4种)
如果是对文本文件的读取和拷贝,推荐使用 字符缓存输入/输出流(BufferedWriter) + 按行读取 (第9种)
对于特大文件,比如 1 个 G 以上的,多线程操作可以使用 NIO (第 10 种)。
一、字节流
1、FileInputStrem 按字节流 逐字节读写(速度最慢)
- /**
- * FileInputStream 方式拷贝文件
- * @param srcFileName
- * @param descFileName
- * @throws IOException
- */
- public static void copyFile(String srcFileName, String descFileName) throws IOException {
- Long startTime = System.currentTimeMillis();
- FileInputStream fis = new FileInputStream(srcFileName);
- FileOutputStream fos = new FileOutputStream(descFileName);
- int len = 0;
- while ((len = fis.read()) != -1) {//读取
- //写入另一个文件
- fos.write(len);
- }
- Long endTime = System.currentTimeMillis();
- System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");//2937
- }
- public static void main(String args[]) throws IOException {
- String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
- String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
- copyFile(srcFileName, descFileName);
- }
总共耗时:2937毫秒
2、FileInputStream 构造一个缓冲数组进行读写(速度提升很多)
- /**
- * FileInputStream 方式拷贝文件,添加缓存数组
- * @param srcFileName
- * @param descFileName
- * @throws IOException
- */
- public static void copyFile(String srcFileName, String descFileName) throws IOException {
- Long startTime = System.currentTimeMillis();
- FileInputStream fis = new FileInputStream(srcFileName);
- FileOutputStream fos = new FileOutputStream(descFileName);
- byte[] buffer = new byte[1024];//构造一个长度为1024的字节数组
- int len = 0;
- while ((len = fis.read(buffer)) != -1) {//读取
- //写入另一个文件
- fos.write(buffer);
- }
- Long endTime = System.currentTimeMillis();
- System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 6
- }
- public static void main(String args[]) throws IOException {
- String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
- String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
- copyFile(srcFileName, descFileName);
- }
总共耗时:6毫秒
3、利用字节缓冲区流 BufferedInputStream 和 BufferedOutputStream来直接逐字节读写(比第1个快很多,比第2个慢)
- /**
- * 使用 BufferedInputStream 和 BufferedOutputStream 通过字符拷贝
- * @param srcFileName
- * @param descFileName
- * @throws IOException
- */
- public static void copyFile(String srcFileName, String descFileName) throws IOException {
- Long startTime = System.currentTimeMillis();
- //缓冲区字节输入流
- BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFileName));
- //缓冲区字节输出流
- BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(descFileName));
- int len = 0;
- while ((len = bis.read()) != -1) {//读取
- //写入另一个文件
- bos.write(len);
- }
- Long endTime = System.currentTimeMillis();
- System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 60
- }
- public static void main(String args[]) throws IOException {
- String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
- String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
- copyFile(srcFileName, descFileName);
- }
总共耗时:60毫秒
4、利用字节缓冲区流BufferedInputStream和BufferedOutputStream通过构造一个缓冲数组进行读写(速度最快)
- /**
- * 使用 BufferedInputStream 和 BufferedOutputStream 通过一个缓冲数组 字符拷贝
- * @param srcFileName
- * @param descFileName
- * @throws IOException
- */
- public static void copyFile(String srcFileName, String descFileName) throws IOException {
- Long startTime = System.currentTimeMillis();
- //缓冲区字节输入流
- BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFileName));
- //缓冲区字节输出流
- BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(descFileName));
- byte[] buffer = new byte[1024];//构造一个长度为1024的字节数组
- int len = 0;
- while ((len = bis.read(buffer)) != -1) {//读取
- //写入另一个文件
- bos.write(buffer);
- }
- Long endTime = System.currentTimeMillis();
- System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 4
- }
- public static void main(String args[]) throws IOException {
- String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
- String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
- copyFile(srcFileName, descFileName);
- }
总共耗时:4 毫秒
二、字符流
5、利用字符流InputStreamWriter和 OutputStreamWriter直接按字节读取
- /**
- * 使用 InputStreamWriter和 OutputStreamWriter直接按字节读取拷贝
- * @param srcFileName
- * @param descFileName
- * @throws IOException
- */
- public static void copyFile(String srcFileName, String descFileName) throws IOException {
- Long startTime = System.currentTimeMillis();
- //缓冲区字符输入流
- InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFileName));
- //缓冲区字符输出流
- OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(descFileName));
- int len = 0;
- while ((len = isr.read()) != -1) {//直接按字节读取
- //写入另一个文件
- osw.write(len);
- }
- Long endTime = System.currentTimeMillis();
- System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 586
- }
- public static void main(String args[]) throws IOException {
- String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
- String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
- copyFile(srcFileName, descFileName);
- }
总共耗时:586毫秒
会乱码
6、字符流InputStreamWriter和 OutputStreamWriter直接用缓冲区数组读写
- /**
- * 使用 InputStreamWriter和 OutputStreamWriter直接按字节读取拷贝
- * @param srcFileName
- * @param descFileName
- * @throws IOException
- */
- public static void copyFile(String srcFileName, String descFileName) throws IOException {
- Long startTime = System.currentTimeMillis();
- //缓冲区字符输入流
- InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFileName));
- //缓冲区字符输出流
- OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(descFileName));
- char[] buffer = new char[1024];//构造一个长度为1024的字节数组
- int len = 0;
- while ((len = isr.read(buffer)) != -1) {//直接按字节读取
- //写入另一个文件
- osw.write(buffer);
- }
- Long endTime = System.currentTimeMillis();
- System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 385
- }
- public static void main(String args[]) throws IOException {
- String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
- String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
- copyFile(srcFileName, descFileName);
- }
总共耗时:385毫秒
7、字符缓冲流BufferedWriter和BufferedReader直接逐字节读写
- /**
- * 使用 BufferedWriter和BufferedReader直接逐字节读写
- * @param srcFileName
- * @param descFileName
- * @throws IOException
- */
- public static void copyFile(String srcFileName, String descFileName) throws IOException {
- Long startTime = System.currentTimeMillis();
- //缓冲区字符输入流
- BufferedReader bf = new BufferedReader(new FileReader(srcFileName));
- //缓冲区字符输出流
- BufferedWriter bw = new BufferedWriter(new FileWriter(descFileName));
- int len = 0;
- while ((len = bf.read()) != -1) {//直接按字节读取
- //写入另一个文件
- bw.write(len);
- }
- Long endTime = System.currentTimeMillis();
- System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 412
- }
- public static void main(String args[]) throws IOException {
- String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
- String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
- copyFile(srcFileName, descFileName);
- }
总共耗时:433毫秒
8、字符缓冲流 BufferedWriter 和 BufferedReader 按照数组大小逐块读写
- /**
- * 使用 BufferedWriter和BufferedReader直接逐字节读写
- * @param srcFileName
- * @param descFileName
- * @throws IOException
- */
- public static void copyFile(String srcFileName, String descFileName) throws IOException {
- Long startTime = System.currentTimeMillis();
- //缓冲区字符输入流
- BufferedReader bf = new BufferedReader(new FileReader(srcFileName));
- //缓冲区字符输出流
- BufferedWriter bw = new BufferedWriter(new FileWriter(descFileName));
- int len = 0;
- char[] buffer = new char[1024];
- while ((len = bf.read(buffer)) != -1) {//直接按字节读取
- //写入另一个文件
- bw.write(buffer);
- }
- Long endTime = System.currentTimeMillis();
- System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 387
- }
- public static void main(String args[]) throws IOException {
- String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
- String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
- copyFile(srcFileName, descFileName);
- }
总共耗时:381毫秒
9、字符缓冲流BufferedWriter和BufferedReader按逐行读写(应用于文本读写)
- /**
- * 使用 BufferedWriter和BufferedReader直接逐字节读写
- * @param srcFileName
- * @param descFileName
- * @throws IOException
- */
- public static void copyFile(String srcFileName, String descFileName) throws IOException {
- Long startTime = System.currentTimeMillis();
- //缓冲区字符输入流
- BufferedReader bf = new BufferedReader(new FileReader(srcFileName));
- //缓冲区字符输出流
- BufferedWriter bw = new BufferedWriter(new FileWriter(descFileName));
- String str;
- while ((str = bf.readLine()) != null) {//直接按字节读取
- //写入另一个文件
- bw.write(str);
- }
- Long endTime = System.currentTimeMillis();
- System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 353
- }
- public static void main(String args[]) throws IOException {
- String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
- String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
- copyFile(srcFileName, descFileName);
- }
总共耗时:353 毫秒
注意:
后面的几种字符流的均会出现中文字符乱码的情况。
需要指定字符集
- InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFileName),"GBK");
- BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(srcFileName),"GBK"));
以上只是通过一个 1MB 的文件的复制测试,并不能说明问题。
总结:
复制文本文件有几种方式?
9种
复制图片文件有几种方式?
4种
他们各自的优缺点是什么?
字符流按字符处理数据,速度较之于同原理的字节流快,但是使用范围较小,仅限文本文件;
字节流处理范围广,文本,音频,视频都可以处理。
三、FileInputStream 与BufferedInputStream区别:
FileInputStream是字节流,BufferedInputStream是字节缓冲流,使用BufferedInputStream读资源比FileInputStream读取资源的效率高(BufferedInputStream的read方法会读取尽可能多的字节),且FileInputStream对象的read方法会出现阻塞;
了解“堵塞”的意思吧!
假设一个文件的长度是100个字节,要将之读取到内存中,再假设您每次只读取10个字节,那么读完整个文件是不是读取10次的呀?
假设老板让你完成100件事情,老板说,你每天只完成10件就可以了,难道你非得等到第十天才完成第100件事情吗?有一天您在中午下班前就完成了10件事情,下午您不妨多干一点,那么也许在第9天的时候就完成了100件事情。
同理,BufferedInputStream有可能会读取比您规定的更多的东西到内存,以减少访问IO的次数,
总之您要记住一句话,访问IO的次数越少,性能就越高,原因就在于CPU和内存的速度》》》》远大于硬盘或其他外部设备的速度。
换一个不太恰当的例子来说,您和您的朋友一起去登山,你朋友太不给力了,走一会儿就要休息,而您呢,您的体力比他要好的多,根本不需要休息,所以每当他休息的时候,您得等着他,您那时候什么也干不了,这就叫堵塞,堵塞就是说您有能力干某事,但是迫于某种原因您什么也干不了,只能干等。所以您朋友休息的次数越少,你们两个到达山顶所花费的时间就越少。CPU访问硬盘的次数越少,程序就越快。BufferedInputStream在小型文件中的性能优势无法体现出来,假设您将以个2G大小的文件从D盘完全复制到E盘,性能之优势便展露无疑!
四、FileInputStream与FileReader区别:
两个类的构造函数的形式和参数都是相同的,参数为 File 对象或者表示路径的 String ,它们到底有何区别呢?
FileInputStream :以字节流方式读取;
FileReader :把文件转换为字符流读入;
InputStream提供的是字节流的读取,而非文本读取,这是和Reader类的根本区别。用Reader读取出来的是char数组或者String ,使用InputStream读取出来的是byte数组。
Reader类及其子类提供的字符流的读取char,inputStream及其子类提供字节流的读取byte,所以FileReader类是将文件按字符流的方式读取,FileInputStream则按字节流的方式读取文件;InputStreamReader可以将读如stream转换成字符流方式,是reader和stream之间的桥梁
最初Java是不支持对文本文件的处理的,为了弥补这个缺憾而引入了Reader和Writer两个类。
FileInputStream 类以二进制输入 / 输出, I/O 速度快且效率搞,但是它的 read ()方法读到的是一个字节,很不利于人们阅读。 而 FileReader 类弥补了这个缺陷,可以以文本格式输入/ 输出,非常方便;比如可以使用 while((ch = filereader.read())!=-1 ) 循环来读取文件;可以使用BufferedReader 的 readLine() 方法一行一行的读取文本。 当我们读写文本文件的时候,采用 Reader 是非常方便的,
比如 FileReader , InputStreamReader 和 BufferedReader 。其中最重要的类是 InputStreamReader ,它是字节转换为字符的桥梁。 你可以在构造器中指定编码的方式,如果不指定的话将采用底层操作系统的默认编码方式,例如 GBK 等。 FileReader 与 InputStreamReader 涉及编码转换 ( 指定编码方式或者采用 os 默认编码 ) ,可能在不同的平台上出现乱码现象!而 FileInputStream 以二进制方式处理,不会出现乱码现象 .
如果处理纯文本文件,建议使用 FileReader ,因为更方便,也更适合阅读;但是要注意编码问题!
其他情况(处理非纯文本文件),FileInputStream是唯一的选择;FileInputStream是进Socket通讯时会用到很多,如将文件流是Stream的方式传向服务器!
五、NIO
10、使用 NIO 复制
- /**
- * NIO
- * @param sourcePath
- * @param targetPath
- * @throws IOException
- */
- public static void copyFile(String sourcePath, String targetPath) throws IOException {
- FileInputStream in = new FileInputStream(sourcePath);
- FileOutputStream out = new FileOutputStream(targetPath);
- FileChannel inChannel = in.getChannel();
- FileChannel outChannel = out.getChannel();
- ByteBuffer buffer = ByteBuffer.allocate(4 * 1024);
- long startTime = System.currentTimeMillis();
- while (inChannel.read(buffer) != -1) {
- buffer.flip();
- outChannel.write(buffer);
- buffer.clear();
- }
- long endTime = System.currentTimeMillis();
- System.out.println("总共耗时" + (endTime - startTime) + "ms");
- }
- public static void main(String args[]) throws IOException {
- String sourcePath = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";
- String targetPath = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";
- copyFile(sourcePath, targetPath);//13ms
- }
IO是阻塞的。NIO是非阻塞的,在多线程的时候效果比较明显。
您可以选择一种方式赞助本站
支付宝扫一扫赞助
微信钱包扫描赞助
赏