如何表示一个空的 InputStream
How to represent an empty InputStream
我正在像这样减少 InputStreams
流:
InputStream total = input.collect(
Collectors.reducing(
empty,
Item::getInputStream,
(is1, is2) -> new SequenceInputStream(is1, is2)));
对于身份 InputStream
,我使用的是:
InputStream empty = new ByteArrayInputStream(new byte[0]);
这行得通,但是有没有更好的方法来表示空 InputStream
?
由于InputStream
只有一个抽象方法,read()
,
public abstract int read() throws IOException
Returns:
the next byte of data, or -1
if the end of the stream is reached.
通过 anonymous subclass 创建空流很容易。
像这样:
InputStream empty = new InputStream() {
@Override
public int read() {
return -1; // end of stream
}
};
但不可否认,它比你的空 ByteArrayInputStream
.
更多代码
我会走另一条路。
通过 (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
时,你会发现这个 class 一点也不神奇,事实上,我们甚至可以通过不使用古老的 class 来做得更好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"); }
这样的语句),它考虑了流的惰性:在遍历所有元素之前关闭时,它不会遍历剩余的流关闭 InputStream
元素,因为此时它们通常甚至没有创建,因此不需要关闭。
现在流的创建就这么简单
InputStream total = new StreamInputStream(input.map(Item::getInputStream));
从 Java 11 开始,您可以使用静态方法 InputStream.nullInputStream()
:
Returns a new InputStream that reads no bytes. The returned stream is initially open. The stream is closed by calling the close() method. Subsequent calls to close() have no effect.
如果您的项目中有 Apache commons-io
:
import org.apache.commons.io.IOUtils
IOUtils.toInputStream("")
我正在像这样减少 InputStreams
流:
InputStream total = input.collect(
Collectors.reducing(
empty,
Item::getInputStream,
(is1, is2) -> new SequenceInputStream(is1, is2)));
对于身份 InputStream
,我使用的是:
InputStream empty = new ByteArrayInputStream(new byte[0]);
这行得通,但是有没有更好的方法来表示空 InputStream
?
由于InputStream
只有一个抽象方法,read()
,
public abstract int read() throws IOException
Returns:
the next byte of data, or-1
if the end of the stream is reached.
通过 anonymous subclass 创建空流很容易。 像这样:
InputStream empty = new InputStream() {
@Override
public int read() {
return -1; // end of stream
}
};
但不可否认,它比你的空 ByteArrayInputStream
.
我会走另一条路。
通过 (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
时,你会发现这个 class 一点也不神奇,事实上,我们甚至可以通过不使用古老的 class 来做得更好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"); }
这样的语句),它考虑了流的惰性:在遍历所有元素之前关闭时,它不会遍历剩余的流关闭 InputStream
元素,因为此时它们通常甚至没有创建,因此不需要关闭。
现在流的创建就这么简单
InputStream total = new StreamInputStream(input.map(Item::getInputStream));
从 Java 11 开始,您可以使用静态方法 InputStream.nullInputStream()
:
Returns a new InputStream that reads no bytes. The returned stream is initially open. The stream is closed by calling the close() method. Subsequent calls to close() have no effect.
如果您的项目中有 Apache commons-io
:
import org.apache.commons.io.IOUtils
IOUtils.toInputStream("")