【问题标题】:How to represent an empty InputStream如何表示一个空的 InputStream
【发布时间】:2018-08-22 04:49:28
【问题描述】:

我正在减少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

【问题讨论】:

  • 在什么方面更好?使用空的BAIS 有什么不好?
  • 只是一个想法......您知道您可以将您的 inputStreams 收集到一个列表中,然后执行SequenceInputStream(Collections.enumeration(yourCollectedList)))?
  • @Kayaman 相当轻微,但会创建一个额外的空字节数组。如果我错过了 Collections.emptyList() 的内容,我最感兴趣的是。
  • 仅供参考,您可以将 (is1, is2) -> new SequenceInputStream(is1, is2) 替换为 SequenceInputStream::new

标签: java java-stream inputstream


【解决方案1】:

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

public abstract int read() throws IOException

回报:
下一个数据字节,如果到达流的末尾,则为 -1

anonymous subclass 很容易创建一个空流。 像这样:

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

但不可否认,它比你空的 ByteArrayInputStream 更多代码。

【讨论】:

  • 这很聪明(至少对我来说),1+
【解决方案2】:

我会走另一条路。

通过(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 时,您会发现这个类并不神奇,事实上,我们甚至可以通过不使用像VectorEnumeration 这样的过时类来做得更好:

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));

【讨论】:

  • 哦,我需要试试这个......我们使用的是第一种方法(正如我在评论中所说),但这看起来更流畅
  • 接近确认...这将影响我们的代码库(再次!)非常感谢
【解决方案3】:

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

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

【讨论】:

  • 请注意InputStream.nullInputStream() 不支持mark,与new ByteArrayInputStream(new byte[0]) 不同。
  • fyi:这个 inputStream 的内容是空字符串
【解决方案4】:

如果您的项目中有 Apache commons-io

import org.apache.commons.io.IOUtils

IOUtils.toInputStream("")

【讨论】:

  • 此用法已弃用!
  • @yusuf 它已被弃用,取而代之的是该方法的更新版本,您可以从文档中找到并使用
猜你喜欢
  • 2021-09-28
  • 2012-01-15
  • 2016-12-19
  • 1970-01-01
  • 2012-12-27
  • 1970-01-01
  • 2012-01-08
  • 2010-09-08
  • 1970-01-01
相关资源
最近更新 更多