java字节流入门(缓冲输出流)

在网上学习java IO流的时候,会发现在 new 一个流时各种嵌套,有的甚至嵌套了三四层,完全不知道是啥套路。之前我们介绍的都是基础流,今天介绍一个可以封装其他流的流 BufferedOutputStream(BFOS)。

之前介绍的 FileOutputStream、ByteArrayOutputStream 等都是基础流,在构造时候不需要传入其他流。但是 BFOS 的构造需要传入一个字节输出流。

基本用法

这个流的功能可以根据名字看出来,是一个缓冲输出流,可以当做一个缓冲区,将输出的字节暂时存在缓冲区里,那么当缓冲区满了之后干嘛呢?那就需要将缓冲的数据都刷出去,刷到另一个流,因此在构造的时候就需要传入一个流。

有点类似游泳池,水满了,就将水全部放掉,再灌新水。为了将水放掉,就需要给一个输出的目的地,这个目的地就是构造方法中需要传入的下游的输出流。其默认的缓冲区大小是 8KB。

java字节流入门(缓冲输出流)_第1张图片

注意这个过程,当写到8KB时,数据还存在 BFOS 中,只要再写一个字节,就将之前缓冲满的全部刷出去。如果缓冲区还没满就想将数据写出去,需要手动 flush。

完整示例如下,这里我们的下游流用了个内存数组流,并随时查看内存数组流的数据量。

java字节流入门(缓冲输出流)_第2张图片

运行结果:

0
8192
8195

之前介绍过列式存储文件格式:大数据的列式存储格式:Parquet

每一列的数据需要在内存中用一个缓冲区管理,如果文件只有一列,那就需要一个缓冲区,如果文件有多列,则需要多个缓冲区。在这里我们用单缓冲区多缓冲区来区分。之前我们的缓冲区用的是 BAOS,参考 java 字节流入门(内存数组流->文件流)。那用 BFOS 可不可以?

单缓冲区

假如我们在内存中只需要维护一个缓冲区的数据,即一个字节数组。当我们想写磁盘时,需要将 FOS 包装一层 BAOS 或 BFOS,为了防止过多小数据量的写操作,因此在内存中做个缓冲,将很多小数据量写操作转化成很少的大数据量写操作,充分利用磁盘的IO。

先比比速度,BFOS+FOS vs BAOS + FOS (内存中只维护一个 BAOS 或一个 BFOS ,并接一个文件输出流)哪种比较快?

做了个实验,将800M数据通过两种方式写到文件中:

BufferedOutputStream + FileOutputStream

java字节流入门(缓冲输出流)_第3张图片

ByteArrayOutputStream + FileOutputStream

java字节流入门(缓冲输出流)_第4张图片

结果:BFOS + FOS 胜出。

7948450e4bb2e48c46e5c4ce3064dcdd.png

BFOS 比 BAOS 还有一个好处,那就是可以控制内存使用,不会无限制占用内存。这是在单个数据缓冲区的时候,即内存中只有一份缓冲数据。

多缓冲区

假如内存中需要维护多个缓冲区,每份缓冲区负责不同的数据,对应到列式存储中就是两列,都需要写到一个文件中,那么 BFOS+FOS 和 BAOS+FOS 这两种搭配有啥区别?

由于 BFOS 的刷文件是由缓冲区满触发的,我们用橙色和红色区分两个缓冲流。假如缓冲流的缓冲区大小为 8KB,每个缓冲流都接收了 24KB 的数据,但是数据并不是均匀来的,因此刷到文件中的顺序是不固定的,也就是每个流会随机触发 3 次 flush。图中 File 里每一小段是 8KB。两个流最多需要的内存是 16KB。

java字节流入门(缓冲输出流)_第5张图片

而使用 BAOS + FOS,假如需要明确将红黄两种数据分开存储,不能互相交叉,则最多需要在内存中缓存 48KB 数据,等一个流缓冲了 24KB 后再写文件。这样就可以将红黄两种数据分隔开,但是先后顺序不确定。

java字节流入门(缓冲输出流)_第6张图片

如果将红黄分开是功能要求,在这种场景下,就必须使用 BAOS。

总结

功能>>性能。为了满足一个场景,首先要找功能达到要求的,其次比较性能。在多份缓冲区时,如果要控制文件格式,需要用 BAOS,在单缓冲区时,可以考虑使用 BFOS,节省内存。

代码:  

https://github.com/qiaojialin/Java-IO-Learning

致谢:东哥

java字节流入门(缓冲输出流)_第7张图片

你可能感兴趣的:(java,python,数据库,jvm,大数据)