【问题标题】:default Stream<E> stream() vs static<T> Stream<T> of(T t)默认 Stream<E> stream() vs static<T> Stream<T> of(T t)
【发布时间】:2017-10-08 18:56:40
【问题描述】:

default Stream&lt;E&gt; stream() 添加到 Collection 接口以支持 Java 8 中的流。Stream 类中的 public static&lt;T&gt; Stream&lt;T&gt; of(T t) 支持类似的功能。 static of() 方法解决的不同目的是什么?

【问题讨论】:

  • 您可以使用可变参数和数组创建 Stream,而不能直接通过 Collection 方式。

标签: functional-programming java-8 java-stream


【解决方案1】:

Collection 接口的方法stream() 可以在现有 集合上调用。作为一个接口方法,它可以被实际的集合实现覆盖,以返回一个适应特定集合类型的Stream

JRE 的标准集合没有利用这个机会,因为默认实现的委托给spliterator() 的策略,这也是一个可覆盖的方法,适合他们的需要。但是文档甚至提到了集合应该覆盖stream()的场景:

spliterator() 方法无法返回IMMUTABLECONCURRENTlate-binding 的拆分器时,应覆盖此方法。


相比之下,static 工厂方法Stream.of(…) 专为没有特定集合的元素数量固定的情况而设计。如果您只需要在可以枚举的元素上添加一个 Stream,则无需创建临时的 Collection

没有可能不同类型的集合,就不需要可覆盖的行为,因此,static 工厂方法就足够了。


请注意,即使您没有可枚举的固定数量的元素,也可以在不需要可重用集合的情况下,为创建单个流的任务提供优化的解决方案:

Stream.Builder<MyType> builder=Stream.builder();
builder.add(A);
if(condition) builder.add(B);
builder.add(C);
builder.build()./* stream operations */

【讨论】:

  • 首先,投票。但Collection.streamStream.of 之间的主要区别在于,第一个是应用模板方法模式,第二个是应用工厂方法模式。它们都是工厂方法。我说的对吗?
  • Collection.stream 也可以被认为满足 abstract factory 模式(即使有默认值)。在软件中识别出多个模式并不罕见。您可以通过使用术语静态工厂方法 作为Stream.of 方法背后的模式名称来限制它。我什至碰巧在我的回答中不自觉地这样做了。我通常更喜欢关注设计的含义,而不是软件设计模式的名称。
  • 谢谢。我认为您的评论比任何答案都好。
【解决方案2】:

据我所知,of 只是一种即时创建 Streams 的实用方法,无需先将元素包装在集合中。

通常提供静态工厂方法of 以跳过数组创建,因为 var-args。例如 java-9 不可变集合提供了这些方法的 许多 重载,例如:

 Set.of()
 Set.of(E)
 Set.of(E e1, E e2)
 .... so on until 11
 Set.of(E... elem)

甚至那些方法的描述是:

虽然这在 API 中引入了一些混乱,但它避免了由可变参数调用引起的数组分配、初始化和垃圾收集开销

由于Stream中只有两种方法:

 Stream.of(T t)
 Streamm.of(T ... values)

我认为可以从var-args 创建流的小型实用方法。

但他们仍然提供了一种创建带有单个元素的 Stream 的方法(而不是留下 var-args 方法),因此对于单个元素,这已经被优化了。

【讨论】:

  • 之所以没有像Set.ofList.of这样的Stream.of方法,是因为后者保证返回一个不可变的集合,因此,不能只保留对可能共享的数组。相比之下,Stream.of(T...) 只是委托给Arrays.stream,因此生成的流将在数组上进行迭代,而无需另一个复制步骤。
【解决方案3】:

从 jdk-8 开始,接口可以添加静态方法/默认方法。您可以在this question 中看到一些在界面上应用模式的示例。

首先,他们都在调用StreamSupport.stream 来创建一个流。

// Collection.stream()
default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

// Stream.of(E)
public static<T> Stream<T> of(T t) {
    return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}

stream() 添加到 Collection 接口是在默认方法中在接口中应用 Template-Method Pattern 的一个很好的例子。你可以看到stream方法调用方法spliterator的源代码,该方法默认实现在Collection接口中。

default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

default Spliterator<E> spliterator() {
    return Spliterators.spliterator(this, 0);
}
Collection 派生的

AND 类可以被覆盖spliterator 实现不同的算法,将使用最高性能的算法。例如spliterator in ArrayList:

public Spliterator<E> spliterator() {
    return new ArrayListSpliterator<>(this, 0, -1, 0);
}

最后,Stream.of() 方法是通过静态方法在接口中应用Factory Method 的一个很好的例子。它是一种从对象实例创建流的工厂方法。

【讨论】:

    【解决方案4】:

    其他答案清楚地解释了Collection.streamStream.of 之间的区别、何时使用其中一种、正在应用哪种设计模式等。@Holger 更进一步并展示了Stream.Builder 的示例用法,我认为它的使用率很低。

    在这里,我想通过混合使用 Stream.ofCollection.stream 方法来补充其他答案。我希望这个例子足够清楚地表明即使Stream.ofCollection.stream 是完全不同的方法,它们也可以一起使用来满足更复杂的需求。

    假设您有N 列表,它们都包含相同类型的元素:

    List<A> list1 = ...;
    List<A> list2 = ...;
    ...
    List<A> listN = ...;
    

    并且您想创建一个包含所有列表元素的流。

    您可以创建一个新的空列表并将所有列表的元素添加到这个新列表中:

    int newListSize = list1.size() + list2.size() + ... + listN.size();
    List<A> newList = new ArrayList<>(newListSize);
    
    newList.addAll(list1);
    newList.addAll(list2);
    ...
    newList.addAll(listN);
    

    然后,您可以在此列表中调用stream(),然后就完成了:

    Stream<A> stream = newList.stream();
    

    但是,您将创建一个中间的、毫无意义的列表,其唯一目的是流式传输原始 list1list2、...、listN 列表的元素。

    更好的方法是使用Stream.of

    Stream<A> stream = Stream.of(list1, list2, ..., listN)
        .flatMap(Collection::stream);
    

    这首先通过枚举每个列表来创建一个列表流,然后通过Stream.flatMap 操作将此列表流平面映射到所有列表元素的流中。因此,在平面映射原始流时会调用Collection.stream

    【讨论】:

    • 首先,投票。合并 Collection 流的好习惯。但你也可以使用Stream.concat(...streams)
    • 经过思考。我认为你的方式很不错。做得好。无需编写排版方法。并用Stream.of(...Collection).flatMap(Collection::stream)复制逻辑
    猜你喜欢
    • 2019-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-29
    • 1970-01-01
    • 2013-12-06
    • 1970-01-01
    相关资源
    最近更新 更多