Java 常见流对象——文件字节流(缓存)

这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战

加缓存之 byte[]

前文说到,要改进 FileInputStream 的读写效率。所以,需要增加一个“袋子”。而这里就可以用,byte[] 来充当袋子的作用。

  • 在 while 的前面加缓存,缓冲区的长度一定是 2 的整数幂,一般用 1024:
1
Java复制代码byte[] buff = new byte[1024];
  • while 里面微调:
1
2
3
4
Java复制代码while ((temp = file.read(buff))!=-1){
System.out.print(temp+" ");
fos.write(buff,0,temp);
}

首先,是 .read() 方法里多了一个缓存区参数,源码如下图:
image.png

其次,是 .write() 方法里的参数需要调整,源码如下图:
image.png
这里我试了一下,图片里的两种写法都是 ok 的,都可以跑出结果。

老师(尚学堂300集Java 后半部分换了另一个老师😭)用了一个较大的图片,然后用肉眼观察这个复制过程,控制台的红色矩形消失的时长。

我心想这样做还非得找个大图片吗?不行,我太懒了。而且,程序员当然要有理性的 量化思维 来算算,这种方式到底比前一种 好在哪里、好了多少

所以那就让我们用高淇老师之前对比过 StringBuilder 和 StringBuffer 效率的计时方法(String 和 StringBuilder 效率对比),来进行一个简单的计时对比。

把上次的代码分别包装到:noneBuffer 函数withBuffer 函数 里。然后前后记内存、计时,再相减。

上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Java复制代码public static void main(String[] args) {

long num_start = Runtime.getRuntime().freeMemory(); // now memory
long time_start = System.currentTimeMillis(); // now time
noneBuffer();
long num_end = Runtime.getRuntime().freeMemory();
long time_end = System.currentTimeMillis();
System.out.println("noneBuffer 占用内存: " + (num_start-num_end));
System.out.println("noneBuffer 占用时间: " + (time_end-time_start));

System.out.println("-------------------------------\n");
long num_start2 = Runtime.getRuntime().freeMemory(); // now memory
long time_start2 = System.currentTimeMillis(); // now time
withBuffer();
long num_end2 = Runtime.getRuntime().freeMemory();
long time_end2 = System.currentTimeMillis();
System.out.println("withBuffer 占用内存: " + (num_start2-num_end2));
System.out.println("withBuffer 占用时间: " + (time_end2-time_start2));

}

运行结果:

image.png

我在 while 里还是保留了 打印 temp 的操作。所以从运行结果的图片也可以很明显的看出来,两次读的时候,每次的 temp 是不一样的。具体对比情况见下表:

比较对象 noneBuffer withBuffer
temp 挨个字节 1024 一包一起
占用内存 2602560 0
占用时间 174 2

可以发现,有缓存区(withBuffer)的情况下比不加缓存区的效率简直完爆,占用的内存和时间都是个位数!

加缓存之 available()

前面那种方法,我们的 buffer 长度是写死的。现在,试试另一种可以相对「智能」地读到这个是什么。也就是 file.available(),源码如下:
image.png
可以看到,返回的是一个在不堵的情况下允许一次性读取的最大长度,但是不堵的情况也有可能会变得堵,比如在网络差的情况下读大文件就会卡。

我已经迫不及待地好奇,这个和上一个技术哪家强了。在稍微更改代码之后,得到以下结果:
iamge.png

withAvailble 函数 的核心代码:

1
2
3
4
5
6
7
8
9
10
11
Java复制代码file = new FileInputStream("/Users/fang/Images/题目.png");
fos = new FileOutputStream("/Users/fang/Images/题目 output3.png");
int temp = 0;
// 创建一个缓冲区,提高读写效率
System.out.println("file.available():" + file.available());
byte[] buff = new byte[file.available()];
file.read(buff);
fos.write(buff);

// 从内存调一下
fos.flush();

下面这条语句,在计时的时候注释掉了。这句是因为我比较好奇,读出来的是什么:
image.png

带这条语句的运行结果:
image.png
发现这个值和前面 1024 的总和一样!可不是嘛,图片是一样的图片,字节的总长度当然一样啦。

下一次再试试缓冲流的威力 ~

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%