我会走另一条路。
通过(is1, is2) -> new SequenceInputStream(is1, is2) 减少大量InputStream 实例可能会创建SequenceInputStream 实例的深度不平衡树,这会变得非常低效。
线性数据结构更合适:
InputStream total = new SequenceInputStream(
Collections.enumeration(input.map(Item::getInputStream).collect(Collectors.toList())));
这将创建一个 SequenceInputStream 处理所有收集的输入流。由于这也本质上处理空列表情况,因此不再需要特殊的空 InputStream 实现。
但是当您查看the source code of SequenceInputStream 时,您会发现这个类并不神奇,事实上,我们甚至可以通过不使用像Vector 和Enumeration 这样的过时类来做得更好:
public class StreamInputStream extends InputStream {
final Spliterator<? extends InputStream> source;
final Consumer<InputStream> c = is -> in = Objects.requireNonNull(is);
InputStream in;
public StreamInputStream(Stream<? extends InputStream> sourceStream) {
(source = sourceStream.spliterator()).tryAdvance(c);
}
public StreamInputStream(InputStream first, InputStream second) {
this(Stream.of(first, second));
}
public int available() throws IOException {
return in == null? 0: in.available();
}
public int read() throws IOException {
if(in == null) return -1;
int b; do b = in.read(); while(b<0 && next());
return b;
}
public int read(byte b[], int off, int len) throws IOException {
if((off|len) < 0 || len > b.length - off) throw new IndexOutOfBoundsException();
if(in == null) return -1; else if(len == 0) return 0;
int n; do n = in.read(b, off, len); while(n<0 && next());
return n;
}
public void close() throws IOException {
closeCurrent();
}
private boolean next() throws IOException {
closeCurrent();
return source.tryAdvance(c);
}
private void closeCurrent() throws IOException {
if(in != null) try { in.close(); } finally { in = null; }
}
}
除了更简单更干净(它不需要像catch (IOException ex) { throw new Error("panic"); }这样的语句)之外,它还考虑了流的惰性:在遍历所有元素之前关闭时,它不会遍历剩余的流来关闭@ 987654333@ 元素,因为此时它们通常甚至没有被创建,因此不需要关闭。
现在流的创建很简单
InputStream total = new StreamInputStream(input.map(Item::getInputStream));