基本概念
作用:当我们需要从多个输入流读取数据时,就可以使用合并流(SequenceInputStream)将其合并一个输入流。
特性:合并流会按照添加的顺序从第一个输入流开始读取,到最后一个输入流结束。
继承:SequenceInputStream 同样地也继承自 InputStream 类
实例探究
1.将两个输入流合并
public static void main(String[] args) throws Exception {
OutputStream os = new FileOutputStream(new File("E:" + File.separator + "os.txt"));
InputStream is1 = new FileInputStream(new File("E:" + File.separator + "is1.txt"));
InputStream is2 = new FileInputStream(new File("E:" + File.separator + "is2.txt"));
//关键 --> 将两个输入添加进合并流
SequenceInputStream sis = new SequenceInputStream(is1, is2);
//按字节数组读取
byte[] buffer = new byte[1024];
int count = 0;
while ((count = sis.read(buffer)) != -1) {
os.write(buffer, 0, count);
}
os.flush();
os.close();
sis.close();
}
观察以上的代码,实现了将两个输入流通过合并流合并成了一个输入流(即将两个 txt 的内容,合并到一个 txt 中),然后再通过输出流进行输出。但这里有个限制,就是每次只允许两个输入流进行合并。若存在多个输入流呢?请接着往下看…
2.将多个输入流合并
public static void main(String[] args) throws Exception {
OutputStream os = new FileOutputStream(new File("E:" + File.separator + "os.txt"));
InputStream is1 = new FileInputStream(new File("E:" + File.separator + "is1.txt"));
InputStream is2 = new FileInputStream(new File("E:" + File.separator + "is2.txt"));
InputStream is3 = new FileInputStream(new File("E:" + File.separator + "is3.txt"));
//关键 --> 通过 Vector 添加多个输入流
Vector<InputStream> vector = new Vector<InputStream>(3);
vector.add(is1);
vector.add(is2);
vector.add(is3);
//将多个输入流添加进合并流
Enumeration<InputStream> en = vector.elements();
SequenceInputStream sis = new SequenceInputStream(en);
//按字节数组读取
byte[] buffer = new byte[1024];
int count = 0;
while ((count = sis.read(buffer)) != -1) {
os.write(buffer, 0, count);
}
os.flush();
os.close();
sis.close();
}
观察以上的代码,发现通过向 Vector 中添加多个输入流,就可以不受数量的限制,实现多个输入流的合并。
源码分析
类结构图
成员变量
//代表添加进 Vector 的所有输入流
Enumeration e;
//代码当前进行操作的输入流
InputStream in;
构造函数,这里定义了 2 个构造函数,分别对用两个输入流以及多个输入流的操作。
// ①允许多个输入流的合并
public SequenceInputStream(Enumeration<? extends InputStream> e) {
this.e = e;
try {
nextStream();
} catch (IOException ex) {
// This should never happen
throw new Error("panic");
}
}
// ②允许两个输入流的合并
public SequenceInputStream(InputStream s1, InputStream s2) {
Vector v = new Vector(2);
v.addElement(s1);
v.addElement(s2);
e = v.elements();
try {
nextStream();
} catch (IOException ex) {
// This should never happen
throw new Error("panic");
}
}
观察它的构造方法,可以看到不论是调用哪种构造函数,其本质都是通过创建 Vector ,然后向其添加输入流(并将值赋给成员变量 e)来实现的。而且两个方法都调用了 nextStream( ) 这个方法。
final void nextStream() throws IOException {
// 不为空,关闭流。说明当前输入流已读取完毕准备下一个输入流的读取
// 不明白可以先往下看
if (in != null) {
in.close();
}
//注意 -->是 if 不是 while
if (e.hasMoreElements()) {
//获取下一个输入流
in = (InputStream) e.nextElement();
if (in == null) {
throw new NullPointerException();
}
} else {
in = null;
}
}
观察代码大概能知道了它的用处是关闭当前输入流(说明我们在代码中可以省略输入流的关闭流操作),并获取下一个输入流。
read 方法,这里定义 2 种读取方式。
public int read() throws IOException {
// 表示 vector 中的输入流已经全部读取完了
if (in == null) {
return -1;
}
int c = in.read();
//读取完输入流,进行递归操作
if (c == -1) {
// 关闭当前输入流,并获取下一个输入流
nextStream();
return read();
}
return c;
}
public int read(byte b[], int off, int len) throws IOException {
// 判断参数的合法性
if (in == null) {
return -1;
} else if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int n = in.read(b, off, len);
if (n <= 0) {
nextStream();
return read(b, off, len);
}
return n;
}
观察代码可以知道 read 方法会依次读取当前的所有输入流,每当读取完一个输入流,就会调用 nextStream 方法获取下一个输入流,然后进行递归返回到 read 方法。直到所有的输入流被读取完后,返回 -1 结束。
剩余方法
//available 方法
public int available() throws IOException {
if (in == null) {
return 0; // no way to signal EOF from available()
}
return in.available();
}
//close 方法
public void close() throws IOException {
do {
nextStream();
} while (in != null);
}