Functional programming 默认流<;E>;流()与静态<;T>;溪流<;T>;of(T)

Functional programming 默认流<;E>;流()与静态<;T>;溪流<;T>;of(T),functional-programming,java-8,java-stream,Functional Programming,Java 8,Java Stream,defaultstream()添加到Collection接口以支持Java 8中的流。类似的功能由流类中的公共静态流(T T)支持。静态of()方法解决问题有什么不同的目的?据我所知,的只是一种动态创建流的实用方法,无需首先将元素包装到集合中 由于变量args的原因,通常会提供静态工厂方法,如的,以跳过数组的创建。例如,java-9不可变集合提供了这些方法的许多重载,如: Set.of() Set.of(E) Set.of(E e1, E e2) .... so on until 11

defaultstream()
添加到
Collection
接口以支持Java 8中的流。类似的功能由
类中的
公共静态流(T T)
支持。静态of()方法解决问题有什么不同的目的?

据我所知,的
只是一种动态创建流的实用方法,无需首先将元素包装到集合中

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

 Set.of()
 Set.of(E)
 Set.of(E e1, E e2)
 .... so on until 11
 Set.of(E... elem)
甚至对这些方法的描述也是:

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

因为流中只有两种方法:

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

我认为可以从<代码> var ARGS<代码>中创建流的小实用方法。 但它们仍然提供了一种方法,可以使用单个元素创建流(而不是只保留var args方法),因此对于单个元素,这已经得到了优化。

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

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

当此方法无法返回不可变的拆分器、
并发的拆分器或后期绑定的拆分器时,应重写此方法


相比之下,
静态
工厂方法(…)
流是为具有固定数量的元素而设计的,而没有特定的集合。当您只需要在可以枚举的元素上创建一个
流时,无需创建一个临时
集合

如果没有潜在不同类型的集合,就不需要重写行为,因此,
static
factory方法就足够了


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

Stream.Builder<MyType> builder=Stream.builder();
builder.add(A);
if(condition) builder.add(B);
builder.add(C);
builder.build()./* stream operations */
Stream.Builder=Stream.Builder();
增加(A);
如果(条件)生成器添加(B);
增加(C);
builder.build()/*流操作*/

自jdk-8以来,接口可以添加静态方法/默认方法。您可以在中看到一些在接口上应用模式的示例

首先,他们都在调用
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);
}
集合
派生的类可以重写
拆分器
实现不同的算法,将使用性能最高的算法。例如
ArrayList
中的
spliterator

public Spliterator<E> spliterator() {
    return new ArrayListSpliterator<>(this, 0, -1, 0);
}
公共拆分器拆分器(){
返回新的ArrayListSpliterator(this,0,-1,0);
}

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

其他答案清楚地解释了
Collection.stream
stream.of
之间的区别,何时使用一种或另一种,应用了哪些设计模式,等等。@Holger更进一步,展示了
stream.Builder
的示例用法,我认为这是高度使用不足


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

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

List<A> list1 = ...;
List<A> list2 = ...;
...
List<A> listN = ...;
但是,您将创建一个中间的、无意义的列表,其唯一目的是流式传输原始
list1
list2
,…,
listN
列表的元素

更好的方法是使用
Stream.of

Stream<A> stream = Stream.of(list1, list2, ..., listN)
    .flatMap(Collection::stream);
Stream=Stream.of(列表1,列表2,…,列表n)
.flatMap(集合::流);

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

您可以使用varargs和数组创建流,而不能直接通过Collection-way。首先,向上投票。但是
Collection.stream
stream.of
之间的主要区别在于,第一个是应用模板方法模式,而第二个是应用工厂方法模式。它们都是工厂方法。我说的对吗?
Collection.stream
也可以被认为是实现抽象工厂模式(即使有默认设置)。在一个软件中识别多个模式并不罕见。您可以使用术语static factory method作为
方法的
流后面的模式名称来限制它。在我的回答中,我甚至无意中做到了这一点。我通常更喜欢关注设计的含义,而不是软件设计模式的名称。谢谢。我想你的评论
int newListSize = list1.size() + list2.size() + ... + listN.size();
List<A> newList = new ArrayList<>(newListSize);

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