如何表示一个空的InputStream

ata*_*lor 12 java inputstream java-stream

我正在减少InputStreams像这样的流:

InputStream total = input.collect(
    Collectors.reducing(
        empty,
        Item::getInputStream, 
        (is1, is2) -> new SequenceInputStream(is1, is2)));
Run Code Online (Sandbox Code Playgroud)

为了身份InputStream,我正在使用:

InputStream empty = new ByteArrayInputStream(new byte[0]);
Run Code Online (Sandbox Code Playgroud)

这有效,但有没有更好的方法来表示空InputStream

Tho*_*sch 16

由于InputStream只有一种抽象方法read(),

public abstract int read() throws IOException

返回:
数据的下一个字节,或者-1如果到达流的末尾.

通过匿名子类很容易创建一个空流.像这样:

InputStream empty = new InputStream() {
    @Override
    public int read() {
        return -1;  // end of stream
    }
};
Run Code Online (Sandbox Code Playgroud)

但诚然,它比你的空代码更多ByteArrayInputStream.


lev*_*tov 9

从Java 11开始,您可以使用静态方法InputStream.nullInputStream()

返回不读取任何字节的新InputStream。返回的流最初是打开的。通过调用close()方法关闭流。随后对close()的调用无效。

  • 请注意,与“new ByteArrayInputStream(new byte[0])”相比,“InputStream.nullInputStream()”不支持“mark”。 (3认同)

Hol*_*ger 6

我会走不同的路线。

通过减少大量InputStream实例(is1, is2) -> new SequenceInputStream(is1, is2)可能会创建一个深度不平衡的SequenceInputStream实例树,这会变得非常低效。

线性数据结构更合适:

InputStream total = new SequenceInputStream(
    Collections.enumeration(input.map(Item::getInputStream).collect(Collectors.toList())));
Run Code Online (Sandbox Code Playgroud)

这将创建一个SequenceInputStream处理所有收集的输入流。由于这也本质上处理空列表情况,因此不再需要特殊的空InputStream实现。


但是当你查看的源代码时SequenceInputStream,你会发现这个类并不神奇,事实上,我们甚至可以通过不使用像Vectorand这样的古老类来做得更好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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

除了更简单和更干净(它不需要像 那样的语句catch (IOException ex) { throw new Error("panic"); }),它还考虑了流的惰性:在遍历所有元素之前关闭时,它不会遍历剩余的流来关闭InputStream元素,因为它们通常不是即使在此时创建,因此不需要关闭。

现在流的创建很简单

InputStream total = new StreamInputStream(input.map(Item::getInputStream));
Run Code Online (Sandbox Code Playgroud)