【问题标题】:How to implement a Java stream?如何实现 Java 流?
【发布时间】:2015-06-06 17:27:18
【问题描述】:

我想实现一个Stream<T>

我不想只使用implements Stream<T>,因为我必须实现大量的方法。

这可以避免吗?

更具体地说,我如何流式传输 t1t2t3,例如:

class Foo<T> {
    T t1, t2, t3;

    Foo(T t1, T t2, T t3) {
        this.t1 = t1;
        this.t2 = t2;
        this.t3 = t3;
    }
}

【问题讨论】:

标签: java java-8 java-stream


【解决方案1】:

JDK对Stream的标准实现是内部类java.util.stream.ReferencePipeline,不能直接实例化。

相反,您可以使用java.util.stream.Stream.builder()java.util.stream.StreamSupport.stream(Spliterator&lt;T&gt;, boolean) 和各种12 其他静态工厂方法来创建默认实现的实例。

使用拆分器可能是最强大的方法,因为它允许您延迟提供对象,同时如果您的源可以分成多个块,还可以实现高效的并行化。

此外,如果您需要实现自己的有状态中间操作,您还可以将流转换回拆分器,将它们包装在自定义拆分器中,然后将它们转换回流 - 例如由于标准 API 的缺点 - 因为大多数可用的中间操作 are not allowed to be stateful
有关示例,请参阅 this SO answer

原则上您可以编写自己的流接口实现,但这会非常乏味。

【讨论】:

    【解决方案2】:

    如果您想创建自己的 Stream 因为您需要自定义 close() 逻辑,最简单的解决方案是从 Iterator 创建 Stream,然后调用 onClose(Runnable)。例如,通过 Jackson 从 Reader 流式传输:

    MappingIterator<?> values = objectMapper.reader(type).readValues(reader);
    return StreamSupport
            .stream(Spliterators.spliteratorUnknownSize(values, Spliterator.ORDERED), false)
            .onClose(() -> {
                try {
                    reader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
    

    【讨论】:

      【解决方案3】:

      您通常不需要编写自己的流类。相反,您可以通过现有方法创建流。例如,这里是如何创建一个值为 1, 100 的流:

        AtomicInteger n = new AtomicInteger(0);
        Stream<Integer> stream = Stream.generate(() -> n.incrementAndGet()).limit(100);
      

      所以在这里我们创建了一个无限的整数流:1、2、3、...。然后我们在这个无限流上使用limit(100) 来获取一个包含 100 个元素的流。

      为清楚起见,如果您想要一个整数流(以固定间隔),您应该使用IntStream.range()。这只是一个示例,展示了如何使用 Stream.generate() 定义流,这为您提供了更大的灵活性,因为它允许您使用任意逻辑来确定 Steam 的元素。

      【讨论】:

      • IntStream.range(0,100) 在这种情况下会更有效率。
      • 这个可以简写为Stream.generate(n::incrementAndGet).limit(100)
      • 如果您希望流从 0 开始,请使用 n::getAndIncrement
      【解决方案4】:

      其他人回答了如何提供通用的Stream 实现。关于您的具体要求,只需这样做:

      class Foo<T> {
      
          T t1, t2, t3;
      
          Foo(T t1, T t2, T t3) {
              this.t1 = t1;
              this.t2 = t2;
              this.t3 = t3;
          }
      
          Stream<T> stream() {
              return Stream.of(t1, t2, t3);
          }
      }
      

      【讨论】:

      • 这个答案既简洁又惯用(至少,它模仿了标准库中的流方法)。所以我会说它应该更高。
      【解决方案5】:

      为了完整起见,因为我没有在 SO 的答案中直接找到这个: 如果您想将现有的 Iterator 转换为 Stream(例如,因为您想连续生成元素),请使用:

      StreamSupport.stream(
          Spliterators.spliterator(myIterator, /* initial size*/ 0L, Spliterator.NONNULL), 
          /* not parallel */ false);
      

      我发现这有点难找,因为您需要了解 StreamSupport、Spliterators 和 Spliterator

      【讨论】:

      • 这很有用,谢谢!请注意,您称为“初始大小”的参数实际上是流的估计大小。不完全确定它的用途,但它似乎在流是否可并行化方面发挥作用,我认为在这里传递 0 完全排除了并发性。 Spliterators.spliteratorUnknownSize(myIterator, Spliterator.NONNULL) 将使用 Long.MAX_VALUE 作为估计值。
      • @sqweek 正如文档所说:“拆分器仅在流管道的终端操作开始后才被遍历、拆分或查询估计大小”。因此,如果 spliterator 的底层迭代器包含的元素多于 size 值,则它们将不会被获取/获取/收集。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多