java字节流入门(缓冲输出流)
在网上学习java IO流的时候,会发现在 new 一个流时各种嵌套,有的甚至嵌套了三四层,完全不知道是啥套路。之前我们介绍的都是基础流,今天介绍一个可以封装其他流的流 BufferedOutputStream(BFOS)。
之前介绍的 FileOutputStream、ByteArrayOutputStream 等都是基础流,在构造时候不需要传入其他流。但是 BFOS 的构造需要传入一个字节输出流。
基本用法
这个流的功能可以根据名字看出来,是一个缓冲输出流,可以当做一个缓冲区,将输出的字节暂时存在缓冲区里,那么当缓冲区满了之后干嘛呢?那就需要将缓冲的数据都刷出去,刷到另一个流,因此在构造的时候就需要传入一个流。
有点类似游泳池,水满了,就将水全部放掉,再灌新水。为了将水放掉,就需要给一个输出的目的地,这个目的地就是构造方法中需要传入的下游的输出流。其默认的缓冲区大小是 8KB。
注意这个过程,当写到8KB时,数据还存在 BFOS 中,只要再写一个字节,就将之前缓冲满的全部刷出去。如果缓冲区还没满就想将数据写出去,需要手动 flush。
完整示例如下,这里我们的下游流用了个内存数组流,并随时查看内存数组流的数据量。
运行结果:
0
8192
8195
之前介绍过列式存储文件格式:大数据的列式存储格式:Parquet
每一列的数据需要在内存中用一个缓冲区管理,如果文件只有一列,那就需要一个缓冲区,如果文件有多列,则需要多个缓冲区。在这里我们用单缓冲区和多缓冲区来区分。之前我们的缓冲区用的是 BAOS,参考 java 字节流入门(内存数组流->文件流)。那用 BFOS 可不可以?
单缓冲区
假如我们在内存中只需要维护一个缓冲区的数据,即一个字节数组。当我们想写磁盘时,需要将 FOS 包装一层 BAOS 或 BFOS,为了防止过多小数据量的写操作,因此在内存中做个缓冲,将很多小数据量写操作转化成很少的大数据量写操作,充分利用磁盘的IO。
先比比速度,BFOS+FOS vs BAOS + FOS (内存中只维护一个 BAOS 或一个 BFOS ,并接一个文件输出流)哪种比较快?
做了个实验,将800M数据通过两种方式写到文件中:
BufferedOutputStream + FileOutputStream
ByteArrayOutputStream + FileOutputStream
结果:BFOS + FOS 胜出。
BFOS 比 BAOS 还有一个好处,那就是可以控制内存使用,不会无限制占用内存。这是在单个数据缓冲区的时候,即内存中只有一份缓冲数据。
多缓冲区
假如内存中需要维护多个缓冲区,每份缓冲区负责不同的数据,对应到列式存储中就是两列,都需要写到一个文件中,那么 BFOS+FOS 和 BAOS+FOS 这两种搭配有啥区别?
由于 BFOS 的刷文件是由缓冲区满触发的,我们用橙色和红色区分两个缓冲流。假如缓冲流的缓冲区大小为 8KB,每个缓冲流都接收了 24KB 的数据,但是数据并不是均匀来的,因此刷到文件中的顺序是不固定的,也就是每个流会随机触发 3 次 flush。图中 File 里每一小段是 8KB。两个流最多需要的内存是 16KB。
而使用 BAOS + FOS,假如需要明确将红黄两种数据分开存储,不能互相交叉,则最多需要在内存中缓存 48KB 数据,等一个流缓冲了 24KB 后再写文件。这样就可以将红黄两种数据分隔开,但是先后顺序不确定。
如果将红黄分开是功能要求,在这种场景下,就必须使用 BAOS。
总结
功能>>性能。为了满足一个场景,首先要找功能达到要求的,其次比较性能。在多份缓冲区时,如果要控制文件格式,需要用 BAOS,在单缓冲区时,可以考虑使用 BFOS,节省内存。
代码:
https://github.com/qiaojialin/Java-IO-Learning
致谢:东哥
- Spring-拾遗
- Golang面试题
- 简单易学的机器学习算法——基于密度的聚类算法DBSCAN
- 厚土Go学习笔记 | 29. 接口
- Golang Template 简明笔记
- hotspare的copyback(r7笔记第30天)
- Spring-AOP
- DBA和开发同事的一些代沟(三)(r7笔记第29天)
- 简单易学的机器学习算法——非线性支持向量机
- 一条关于swap争用的报警邮件分析(一)(r7笔记第28天)
- Spring-IOC(2)
- Python3 pandas read_csv 读取txt文件报错:IOError: Initializing from file failed
- dataguard添加临时数据文件的bug(r7笔记第27天)
- 简单易学的机器学习算法——线性支持向量机
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Laravel 关联模型-关联新增和关联更新的方法
- 如何解决PHP获取不到SESSION信息之一般情况
- PHP实现通过二维数组键值获取一维键名操作示例
- laravel 判断查询数据库返回值的例子
- laravel框架数据库配置及操作数据库示例
- laravel 输出最后执行sql 附:whereIn的使用方法
- laravel框架模型、视图与控制器简单操作示例
- Laravel关系模型指定条件查询方法
- 在laravel中使用with实现动态添加where条件
- laravel Model 执行事务的实现
- 在laravel中实现事务回滚的方法
- thinkphp5框架实现数据库读取的数据转换成json格式示例
- phpfpm的作用和用法
- 浅谈PHP5.6 与 PHP7.0 区别
- laravel执行php artisan migrate报错的解决方法