概述
今天给大家分享一个比较有意思的 IO 流。SequenceInputStream ,听名字顾名思义。
SequenceInputStream 流可以把多个 InputStream 合并为一个 InputStream . 按照指定的顺序,把几个输入流连续地合并起来,使用起来像一个流一样。并且使它们像单个输入流一样出现。每个输入流依次被读取,直到到达该流的末尾。然后“序列输入流”类关闭这个流并自动地切换到下一个输入流。
合并流的作用是将多个源合并合一个源。
使用场景
比如现在有三个文件【1.txt】、【2.txt】、【3.txt】;现在要把这三个文件按照1、2、3的顺序合并成一个文件输出到 【all.txt】文件中。
如果不知道有这个流,大家可能都是自己一个一个文件的去读,自己合并到一个文件中。
有了这个流,我们操作起来,代码更加优雅。
示例
public class SequenceInputStreamDemo {
public static void sequenceStream() {
// 创建字节输入流对象s1,s2,s3
try (InputStream s1 = new FileInputStream(new File("d:\\1.txt"));
InputStream s2 = new FileInputStream(new File("d:\\2.txt"));
InputStream s3 = new FileInputStream(new File("d:\\3.txt"));) {
/**
* SequenceInputStream(Enumeration<? extends InputStream> e)
* 通过记住参数来初始化新创建的 SequenceInputStream, 该参数必须是生成运行时类型为 InputStream
* 对象的 Enumeration 型参数。
*/
// 创建一个Vector类对象v
Vector<InputStream> v = new Vector<>();
/**
* void addElement(E obj) 将指定的组件添加到此向量的末尾,将其大小增加 1。
*/
// 将3个字节流对象添加到Vector容器中
v.addElement(s1);
v.addElement(s2);
v.addElement(s3);
// 获取Vector对象中的元素
Enumeration<InputStream> e = v.elements();
// 将Enumeration对象中的流合并(创建一个序列流,用于合并多个字节流文件s1,s2,s3)
try (SequenceInputStream se = new SequenceInputStream(e);
OutputStream os = new FileOutputStream("d:\\all.txt")) {
byte[] b = new byte[1024];
int len = 0;
while ((len = se.read(b)) != -1) {
os.write(b, 0, len);
os.write("\r\n".getBytes());
}
System.out.println("合并成功");
} catch (IOException ex) {
ex.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
sequenceStream();
}
}
该示例是,把D盘目录下的三个文件【1.txt】、【2.txt】、【3.txt】使用SequenceInputStream 流,按照 指定的文件的顺序合并输出到 【all.txt】 文件中。
SequenceInputStream 源码解析
public class SequenceInputStream extends InputStream {
Enumeration<? extends InputStream> e;
InputStream in;
......
}
SequenceInputStream 继承 InputStream。
定义了一个 Enumeration 的一个变量。该变量存储的是所有需要合并的InputStream。
定义了一个 InputStream的一个变量。该变量是记录 Enumeration 中正在读去的 InputStream。
Enumeration
Enumeration 接口是Iterator迭代器的“古老版本”,从JDK 1.0开始,Enumeration接口就已经存在了(Iterator从JDK 1.2才出现)。Enumeration接口只有两个方法。
构造方法

提供两个构造方法。
- 1、提供 Enumeration 类型的参数。
把传过来的参数赋值给 e 变量。
并调用 nextStream() 方法。
现在的迭代器都在使用Iterator,而这里为啥还在使用Enumeration 呢?
因为 Iterator 是在JDK1.2以后才有的,而SequenceInputStream 这个流在JDK1.0就已经存在了,故使用 Enumeration 当参数。
- 2、提供两个 InputStream 类型的参数。
构造中把这两个流添加到 Vector 数组中,然后调用 elements 方法放回一个 Enumeration 类型,并赋值给 e 变量。
并调用 nextStream() 方法。
nextStream 方法

1、判断该流是否不为null,不为null 则把当前 InputStream 关闭。
2、该方法是先判断 Enumeration 中是否有可用的元素,如果获取 Enumeration 迭代器中下一个的元素,并赋值给 in 变量。
read 方法

读取 InputStream 中的数据。如果当前 InputStream 读取完了,再调用 nextStream 方法,获取下一个流,然后继续读取。直到所有的流都已经读取完毕。
close 方法

close 方法会关闭剩下没有读取完的所有的 InputStream。
读取过的InputStream 会在 调用 nextStream 方法中关闭。所以在这只需要关闭没有读取完的流就可以了。
看完源码是不是感觉 SequenceInputStream 超级简单,自己也可以实现一个。
喜欢本文的朋友们,欢迎长按下图关注订阅号 java404,收听更多精彩的内容
网友评论