JAVA流的使用(复制文件效率对比)初学者

像什么docx啊,什么xlsx呀的,都要插入图片视频什么的吧?那插入的这些东西,如果我们也用字符流来复制的话就会造成文件损坏。所以一般来说,除了txt文件用字符流(Reader 和 Writer,操作纯文本)外,其他的文件我建议用字节流来复制。

一、先来看一段在我学习时老师教的常规的复制文件方法

四步曲:(注:main方法抛 IO 异常)

//1.定义指针指向文件:
File fIn = new File("F:\\LCY\\LCY  ujqi\\珍藏\\次仁央宗 - 为你等待.mp3");//得到文件
File fOut = new File("F:\\LCY\\LCY ipnu ethw\\自学\\java培训\\培训代码\\test\\次仁央宗 - 为你等待.mp3");//输出到哪个位置

//定义开始计时时间:
long start = System.currentTimeMillis();

//2.定义流指向指针:
InputStream is = new FileInputStream(fIn);//输入流
OutputStream os = new FileOutputStream(fOut);//输出流

//3.复制操作:
int k = 0;
while ((k = is.read()) != -1) {
   os.write(k);
}

//4.关闭流
os.close();
is.close();

//6.定义结束计时时间:
long end = System.currentTimeMillis();

//看看所需要的时间:
System.out.println("复制文件成功。" +
   "文件大小:" + (fIn.length() / 1024 / 1024) + " MB," +
   "用时:" + (end - start) + " ms");
		注意(指针的解释):
			我之前也学过 C++ ,里面有一个重要的知识点叫"指针"。
				我的课本里解释:指针就是地址。
				我的老师也说:还不知道指针?指针就是地址啊!!
			
			当我学习 java 时,老师就讲到:在java里面没有指针这个说法。
			
			当时我就在想:
				那我们的像上面定义的这个 fIn / fOut 是什么?
				它不是也是指向这个东西吗?
				或者具体来说,它也是指向内存存储文件的这个空间呀?
				
			后来我的理解是这样的:
				不管是 C++ 也好,还是 java 也好,实际上都是有指针这个东西的。
				指针应该这样理解:
					指针不是地址,而是指向计算机地址的一个媒介。
					计算机通过指针,可以访问这个地址。
					
			由于 java 里没有指针这个说法,所以我所说的指针实际上是指:
				通过这个定义的变量来访问这个文件。

好了,看看结果

"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=49749:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:9 MB,用时:40654 ms

Process finished with exit code 0

如上,字节流复制文件,10M的东西,40秒,成功了。

上述代码的简写

计算机干的事情是:你告诉我想要什么,我给你。而且他没有思想,不会厌烦,相反他最喜欢干。举个例子:

p1:“给个箱子,我存钱!”				计算机:“double money = 0; 好,给!”

p2:“给些流,我要复制文件!”			计算机:“Reader、Writer、Stream,都在这,自己选!”

p3:“给个女朋友!”					计算机:“女朋友 女朋友 = new 女朋友("闭月羞花"); 好了,给你 new 个最好的,拿去,不用谢!”

看到没有?计算机很乐于去作这种你输入需求,我闭着眼睛从百宝箱里拿出来丢给你的这种事情,关键是他还不会出错。但我们就不一样。拿这个复制文件的流举个例子:

定义指针定义流 ~ 复制文件关闭流。。。
每次都是这几步,好,计算机给你流了。但当有一天,你忘记还给计算机了,那计算机的百宝箱是不是就缩水了?积少成多,最后造成的结果是不是百宝箱空空如也了?

为了解决我们这个有头无尾的坏毛病,大佬就在 JDK 7 时让虚拟机自动帮我们关闭流,好,例子在这

		File fIn = new File("F:\\LCY\\LCY  ujqi\\珍藏\\次仁央宗 - 为你等待.mp3");//得到文件
		File fOut = new File("F:\\LCY\\LCY ipnu ethw\\自学\\java培训\\培训代码\\test\\次仁央宗 - 为你等待.mp3");//输出到哪个位置
		
		long start = System.currentTimeMillis();//得到系统时间(开始计时)
		
		try (
				InputStream is = new FileInputStream(fIn);//输入流
				OutputStream os = new FileOutputStream(fOut);//输出流
		) {
			int k = 0;
			while ((k = is.read()) != -1) {
				os.write(k);
			}
		}
		
		long end = System.currentTimeMillis();//得到系统时间(结束计时)
		System.out.println("复制文件成功。" +
				"文件大小:" + (fIn.length() / 1024 / 1024) + " MB," +
				"用时:" + (end - start) + " ms");

如上所示,我们只需要写入一个try,把需要关闭的资源放到小括号(…)里面,把资源的操作放到大括号{…}里面,我们就可以大刀阔斧地使用资源了,你用完了,撂担子走人就好,计算机自己会把属于他的资源拿回来的。

因为这种办法简单,所以我下面就只使用这种办法来写了。

那么有个问题,这种方法只有10M的东西,就要40秒了哦!——“才40秒,没关系,哥有的是时间!” 好,1个G呢??

所以,正确地使用I / O流 来复制文件是学java中特别有必要的一件事。

二、接着,浅谈一下缓冲流

缓冲流是什么?

最近我们家晒谷子了,就就地取材用谷子举例吧

天要下雨了,我们要把谷子收到麻袋里。
那么字节流这样的,就相当于你一把谷子一把谷子地捧起来放到麻袋,那你还要不要你家的谷子?你品,你细品。

缓冲流就像是你拿一个铲子,一铲一大把,这效率是不是就变高了?

好了,直接上代码

		File fIn = new File("F:\\LCY\\LCY  ujqi\\珍藏\\次仁央宗 - 为你等待.mp3");//得到文件
		File fOut = new File("F:\\LCY\\LCY ipnu ethw\\自学\\java培训\\培训代码\\test\\次仁央宗 - 为你等待.mp3");//输出到哪个位置

		long start = System.currentTimeMillis();//得到系统时间(开始计时)

		try (
				InputStream is = new FileInputStream(fIn);//输入流
				OutputStream os = new FileOutputStream(fOut);//输出流

				BufferedInputStream bis = new BufferedInputStream(is);//缓冲输入流
				BufferedOutputStream bos = new BufferedOutputStream(os);//缓冲输输出流
		) {
			byte[] bytes = new byte[1024];//长度数组,每次读 1KB 的长度

			int k = 0;
			while ((k = bis.read(bytes)) != -1) {
				bos.write(bytes, 0, k);
				/*
					bos.write(
						bytes: 写什么
						0: 从哪开始写
						k: 写多少
					)
				 */
			}
		}

		long end = System.currentTimeMillis();//得到系统时间(结束计时)
		System.out.println("复制文件成功。" +
				"文件大小:" + (fIn.length() / 1024 / 1024) + " MB," +
				"用时:" + (end - start) + " ms");

上述代码中,我们比之前多了try(…)代码块的两个缓冲流、一个长度的数组,而且复制操作中的 k = is.read() 改成了 k = bis.read(bytes) ,下面的写操作 os.write(k) 改成了 bos.write(bytes, 0, k)

那么这就是一个完整的缓冲流复制文件了,上重点

"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=65181:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:9 MB,用时:64 ms
Process finished with exit code 0

您看,一样的文件,字节流要40654ms,缓冲流64ms,这种效率,您家谷子是不是可以免受大雨的 “社会毒打” 之苦了?

但是,缓冲这东西嘛,并不是说越大越好。就像你铲谷子,1L 容量的太慢,但是 100L 的你拿的起吗?你装进铲子里头都费劲,到头来是不是更慢?好嘞,那我们就来测试一下,对于计算机来说,究竟用多大容量的 “铲子” 来铲谷子是最合适的。

我们来个大文件玩玩,我电脑里目前最大的文件就是这个,1.5G,差不多够用了,就不搞镜像文件这东西了

先来个缓冲 1KB
		File fIn = new File("F:\\LCY\\LCY tcpn\\temp\\阅\\cut_012203_164_0_1.mp4");
		File fOut = new File("F:\\LCY\\LCY ipnu ethw\\自学\\java培训\\培训代码\\test\\cut_012203_164_0_1.mp4");
		
		long start = System.currentTimeMillis();//得到系统时间(开始计时)
		try (
				InputStream is = new FileInputStream(fIn);//输入流
				OutputStream os = new FileOutputStream(fOut);//输出流
				
				BufferedInputStream bis = new BufferedInputStream(is);//缓冲输入流
				BufferedOutputStream bos = new BufferedOutputStream(os);//缓冲输输出流
		) {
			byte[] bytes = new byte[1024];//长度数组,每次读 1KB 的长度
			int k = 0;
			while ((k = bis.read(bytes)) != -1) {
				bos.write(bytes, 0, k);
			}
		}
		
		long end = System.currentTimeMillis();//得到系统时间(结束计时)
		System.out.println("复制文件成功。" +
				"文件大小:" + (fIn.length() / 1024 / 1024) + " MB," +
				"用时:" + (end - start) + " ms");

结果看下面

"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=54078:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:39011 ms

Process finished with exit code 0

好了,要39秒。

下一个,1MB

不啰嗦,就只改长度数组那一行

byte[] bytes = new byte[1024 * 1024];//每次读 1MB 的长度

得到结果:

"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=54078:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:15335 ms

Process finished with exit code 0

看,15秒。

下一个,10M
byte[] bytes = new byte[1024 * 1024 * 10];//每次读 10MB 的长度

结果:

"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=54508:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:13824 ms

Process finished with exit code 0

14秒,几乎没区别了。

下一个,20M
byte[] bytes = new byte[1024 * 1024 * 20];//每次读 20MB 的长度
"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=54567:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:21226 ms

Process finished with exit code 0

好的,21秒。那么可以看到:到 20M 缓冲时,复制的效率慢下来了,那也就是说,速度最快的缓冲是在 [1MB, 20MB]区间内。

我们用二分法来看看。
a.5MB
			byte[] bytes = new byte[1024 * 1024 * 5];//每次读 5MB 的长度

结果如下

"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=54755:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:15740 ms

Process finished with exit code 0
b.5M 的速度和 1M 的差不多,再看看 15M 的
byte[] bytes = new byte[1024 * 1024 * 15];//每次读 15MB 的长度
"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=54827:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:15549 ms

Process finished with exit code 0

也是15秒,那也就是说 速度最快的缓冲就是 10M 的了。但这只是理论上的。实际上,计算机瞬时读取系统时间时也是有误差的,就那么一瞬间,几十毫秒就溜过去了。而在我之前测的一个文件是600M,测了5次,总结出速度是 5MB > 10MB > 1MB

所以,缓冲流中,我推荐的缓冲数组长度是 [5MB, 10MB] 这个区间。

那我一直在这里做,我还是个现代人吗?现在的人工智能跟我无缘了吗?java我待你不薄,你为什么要这样对我????

在这里插入图片描述

你放心,社会没有抛弃你,java也没有抛弃你,java也有人工智能的,不要你面朝黄土背朝天地干!介绍两个,异步和通道。

异步是什么?

异步就是,你雇了个人,在那帮你收谷子,你坐在旁边守着。如果你上三急去上厕所,恰好他在这个时间段就把谷子收好了,那他就坐在那里,等你上完厕所回来给他结工资。

好了,介绍完了,拿刀来!!

		File fIn = new File("F:\\LCY\\LCY tcpn\\temp\\阅\\cut_012203_164_0_1.mp4");
		File fOut = new File("F:\\LCY\\LCY ipnu ethw\\自学\\java培训\\培训代码\\test\\cut_012203_164_0_1.mp4");
		
		long start = System.currentTimeMillis();//得到系统时间(开始计时)
		
		//复制操作
		Files.copy(
				fIn.toPath(),//输入指针的路径
				fOut.toPath(),//输出指针的路径
				StandardCopyOption.COPY_ATTRIBUTES,//复制属性
				StandardCopyOption.REPLACE_EXISTING//替换现有的
		);
		
		long end = System.currentTimeMillis();//得到系统时间(结束计时)
		System.out.println("复制文件成功。" +
				"文件大小:" + (fIn.length() / 1024 / 1024) + " MB," +
				"用时:" + (end - start) + " ms");

可以看到,这个异步操作,就是把try(…){…}的东西换成了一句话 (我这里为了易看,就将每个参数写成了一行) 。好,看结果

"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=56947:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:40393 ms

Process finished with exit code 0
40秒,和缓冲流的 1KB 缓冲不差多少。
再说一下通道。

所谓通道,就是你雇个人干活,你不用在旁边守着了,回去打你王者荣耀,吃你鸡,追你剧去。啥?你问他干完活了后怎么给钱?微信呀,说好的面向现代化呀!!!

二爷青龙偃月刀在此!

		File fIn = new File("F:\\LCY\\LCY tcpn\\temp\\阅\\cut_012203_164_0_1.mp4");
		File fOut = new File("F:\\LCY\\LCY ipnu ethw\\自学\\java培训\\培训代码\\test\\cut_012203_164_0_1.mp4");
		
		long start = System.currentTimeMillis();//得到系统时间(开始计时)
		
		//复制操作
		try (
				FileInputStream fis = new FileInputStream(fIn);//输入流
				FileOutputStream fos = new FileOutputStream(fOut);//输出流
		) {
			//各自获取各自的可读可写通道
			FileChannel fcRead = fis.getChannel();
			FileChannel fcWrite = fos.getChannel();
			
			fcRead.transferTo(0, fcRead.size(), fcWrite);//transferTo:将该通道的文件字节传到可写通道
		}
		
		long end = System.currentTimeMillis();//得到系统时间(结束计时)
		System.out.println("复制文件成功。" +
				"文件大小:" + (fIn.length() / 1024 / 1024) + " MB," +
				"用时:" + (end - start) + " ms");

好了,结果如下

"C:\Program Files\Java\jdk-12.0.2\bin\java.exe" -javaagent:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\lib\idea_rt.jar=57881:E:\ideaIU-2019.3.3\ideaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath E:\eclipse\HomeWorks\复制效率\bin 复制效率.Test
复制文件成功。文件大小:1529 MB,用时:40757 ms

Process finished with exit code 0
也是40秒,没多少大变化。那我要他何用?重点是你不需要亲力亲为了撒,面向现代化了撒。哪个简单?

在这里插入图片描述

总结

如果说要速度的话,我选择用缓冲流的 5MB 和 10MB 大小;如果说要方便的话,我会用通道的方法去做。

后言

首篇博客,修改过。写得不好,虚心求教各位大神!!
非常感谢您的观看!您的收获就是我的最大动力!!再次感谢!!!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值