基于Java8的java.util.stream包说明文档

用于支持对元素流进行函数式风格操作的类,例如对集合的map-reduce转换。

目录

一、接口概要

二、类摘要

三、枚举摘要

四、java.util.stream包说明

1、对Collection进行Stream的filter-map-reduce操作的例子

2、Stream与集合不同的地方

3、获得Steam的方式

五、流操作和管道

中间操作、终端操作

延迟处理

无状态、有状态

短路

1、并行性

串行和并行操作

用户行为参数:无干扰、无状态

2、无干涉

3、无状态行为

4、副作用

5、排序

六、Reduction操作

归约(reduction)操作

使用reduce运算的原因

例子

1、可变的reduction

可变reduction操作collect()

抽象的方式表达collect的好处

Collector的使用

collect()并行化条件

2、Reduction、并发 和 排序

并发reduction 

Collector.Characteristics.CONCURRENT特性标记

3、Associativity

七、低级stream构造

类StreamSupport和Spliterator

文章来源


 

一、接口概要

接口

说明

 

BaseStream<T,S extends BaseStream<T,S>>

流的基本接口,是支持顺序和并行聚合操作的元素序列。

Collector<T,A,R>收集器

一种可变的归约操作,它将输入元素收集到可变结果容器中,在处理完所有输入元素后,可以选择地将收集的结果转换成最终表示形式。

DoubleStream

支持顺序和并行聚合操作的原始double类型值元素序列。

DoubleStream.Builder

DoubleStream的可变建造者。

IntStream

支持顺序和并行聚合操作的原始int类型值元素序列。

IntStream.Builder

IntStream的可变建造者。

LongStream

一种支持顺序和并行聚合操作的原始long类型值元素序列。

LongStream.Builder

LongStream的可变建造者

Stream<T>

支持顺序和并行聚合操作的元素序列。

Stream.Builder<T>

Stream的可变建造者。

二、类摘要

说明

Collectors

Collector接口的实现,实现各种有用的 reduction 操作,例如收集元素到集合中,根据各种条件汇总元素,等等。

StreamSupport

用于创建和操作流的低级实用程序方法。

三、枚举摘要

枚举

说明

Collector.Characteristics

收集器.特性

Collector指示收集器属性的特征,可用于优化reduction 实现。

四、java.util.stream包说明

用于支持对元素流进行函数式风格操作的类,例如对集合的map-reduce转换。

1、对Collection进行Stream的filter-map-reduce操作的例子

int sumOfWeights = widgets.stream()
	.filter(b -> b.getColor() == RED)
	.mapToInt(b -> b.getWeight())
	.sum();

在这里,我们使用Collection<Widget>作为流的源,然后对流执行filter-map-reduce以获取红色widgets的weight的总和。(求总数是 reduction 运算的一个示例。)

2、Stream与集合不同的地方

此软件包中引入的关键抽象是stream。类StreamIntStream, LongStream,和DoubleStream 覆盖了Object,原始int,long和 double类型。Stream在几个方面与集合有所不同:

  • 没有存储。Stream不是存储元素的数据结构。相反,它通过计算操作管道从源(如数据结构、数组、生成器函数或I/O通道)传输元素。
  • 函数式的。对Stream的操作会产生结果,但不会修改其源。例如,过滤从集合获得的Stream会生成一个没有过滤元素的新Stream,而不是从源集合中删除元素。
  • 追求懒惰。许多Stream操作(例如过滤filtering,映射mapping或重复删除duplicate removal)可以延迟实施,从而暴露出优化的机会。例如,“查找具有三个连续元音的第一个字符串”不需要检查所有输入字符串。Stream操作分为中间操作(产生Stream)和最终操作(产生值或副作用)。中间操作总是很懒惰。
  • 可能无限。尽管集合的大小是有限的,但Steam却不需要有大小界限。诸如limit(n)或findFirst()之类的短路操作可以使无限流上的计算在有限时间内完成。
  • 消耗品。Stream的元素在Stream的生命周期中只访问一次。像迭代器一样,必须生成一个新的Stream来重新访问源的相同元素。

3、获得Steam的方式

Stream可以通过多种方式获得。一些示例包括:

  • 通过stream()与 parallelStream()方法从Collection中获得Stream;

使用这些技术的第三方库可以提供额外的Steam源。

五、流操作和管道

中间操作、终端操作

流操作分为中间操作和 终端操作,并合并以形成Stream管道。流管道由源(例如 Collection,数组,生成器函数或I / O通道)组成;随后是零个或多个中间操作,例如 Stream.filter或Stream.map;以及诸如Stream.forEach或的终端操作Stream.reduce。

中间操作返回一个新的Stream。他们总是 懒惰的; 执行诸如filter()之类的中间操作实际上并不执行任何过滤,而是创建一个新的Stream,当被遍历时,它包含与给定断言匹配的初始Stream的元素。在执行管道的终端操作之前,不会开始遍历管道源。

终端操作(例如Stream.forEach或 IntStream.sum)可能会遍历Stream以产生结果或副作用。执行终端操作后,Stream管道被视为已消耗,无法再使用;如果需要再次遍历相同的数据源,则必须返回到数据源以获取新的流。在几乎所有情况下,终端操作都很渴望在返回之前完成对数据源的遍历和对管道的处理。只有终端操作iterator()和spliterator()不是; 在现有操作不足以满足任务的情况下,这些提供为“逃生舱”,以允许任意客户端控制的管道遍历。

延迟处理

延迟处理流可显着提高效率;在上面的filter-map-sum示例的管道中,可以将过滤,映射和求和融合到数据的一次传递中,而中间状态最少。懒惰还可以避免在不必要时检查所有数据。对于诸如“查找长度超过1000个字符的第一个字符串”之类的操作,只需检查足够多的字符串以查找具有所需特征的字符串,而无需检查可从源中获得的所有字符串。(当输入流无限且不仅仅是大时,此行为就变得更加重要。)

无状态、有状态

中间操作又分为无状态操作 和有状态操作。无状态操作(如filter和map)在处理新元素时不保留以前看到的元素的任何状态——-每个元素都可以独立于其他元素上的操作进行处理。有状态操作(例如 distinct和)sorted在处理新元素时可以合并先前看到的元素的状态。

有状态操作可能需要在产生结果之前处理整个输入。例如,在查看流的所有元素之前,不能对流进行排序产生任何结果。因此,在并行计算下,某些包含有状态中间操作的管道可能需要对数据进行多次遍历,或者可能需要缓冲大量数据。包含完全无状态中间操作的管道可以在单个过程中进行处理,无论是顺序的还是并行的,只需最少的数据缓冲。

短路

此外,一些操作被认为是短路操作。如果出现无限输入时,中间操作可能会短路,这可能会导致产生有限的流。如果出现无限输入时,端子操作可能会在有限时间内终止,则该端子操作会发生短路。在管道中进行短路操作是使无限流的处理在有限时间内正常终止的必要条件,但不是充分条件。

1、并行性

串行和并行操作

具有显式for循环的处理元素本质上是串行的。流通过将计算重构为聚合操作的管道来促进并行计算,而不是将其作为每个单独元素上的必要性操作来执行。

所有流操作都可以串行或并行执行。除非明确要求并行性,否则JDK中的流实现将创建串行流。例如,Collection具有方法 Collection.stream()Collection.parallelStream(),分别产生顺序流和并行流;其他承载流的方法(例如IntStream.range(int, int) 产生顺序的流),但是可以通过调用它们的BaseStream.parallel()方法来有效地并行化这些流。要并行执行先前的“widgets的weight的总和”查询,我们将执行以下操作:

int sumOfWeights = widgets.parallelStream()
	.filter(b -> b.getColor() == RED)
	.mapToInt(b -> b.getWeight())
	.sum();

此示例的串行和并行版本之间的唯一区别是使用“ parallelStream()”代替“ stream()”创建初始流。启动终端操作时,根据被调用的流的方向,顺序或并行地执行流管道。流是串行执行还是并行执行可以用isParallel()方法确定,并并且可以使用BaseStream.sequential()BaseStream.parallel()操作修改流的方向。当终端操作被启动时,根据其被调用的流的模式,顺序地或并行地执行流管道。

串行和并行是否影响结果

除了显示地被标识为非确定新的操作,例如findAny(),流是顺序执行还是并行执行不应改变计算结果。

用户行为参数:无干扰、无状态

大多数流操作接受描述用户指定行为的参数,这些参数通常是lambda表达式。为了保留正确的行为,这些行为参数必须是无干扰的,并且在大多数情况下必须是无状态的。此类参数始终是函数式接口(如Function )的实例,并且通常是lambda表达式或方法引用。

2、无干涉

Stream允许您能够对各种数据源执行可能并行的聚合操作,甚至包括非线程安全的集合(例如 ArrayList)。只有在执行流管道时,我们才有可能防止对数据源干扰。除了逃生舱操作iterator()和 spliterator()之外,执行从调用终端操作开始,在终端操作完成时结束。对于大多数数据源,防止干扰意味着确保在执行流管道期间数据源根本不被修改。这方面的显著例外是源为并发集合的流,这些流是专门为处理并发修改而设计的。并发流源是那些Spliterator报告其 CONCURRENT特征的流源。

因此,源可能不是并发的,流管道中的行为参数永远不应修改流的数据源。如果行为参数修改或导致修改流的数据源,则称其干扰非并发数据源。无干扰的需求适用于所有管道,而不仅仅是并行管道。除非流源是并发的,否则在流管道执行期间修改流的数据源可能会导致异常,错误答案或不一致的行为。对于行为良好的流源,可以在终端操作开始之前对源进行修改,并且这些修改将反映在所涵盖的元素中。例如,考虑以下代码:

    List<String> l = new ArrayList(Arrays.asList("one", "two"));
    Stream<String> sl = l.stream();
    l.add("three");
    String s = sl.collect(joining(" "));

首先,创建一个由两个字符串组成的列表:“ one”; 和“two”。然后从该列表创建一个流。接下来,通过添加第三个字符串“three”来修改列表。最终,将流中的元素收集起来并结合在一起。由于列表是在终端collect 操作开始之前修改的,因此结果将是字符串“one two three”。从JDK集合以及大多数其他JDK类返回的所有流都以这种方式表现良好。有关其他库生成的流的信息,请参阅 低级Stream构造Low-level stream construction 以了解构建行为良好的流的要求。

3、无状态行为

如果流操作的行为参数是有状态的,流管道结果可能不确定或不正确。 有状态的lambda(或实现适当的功能接口的其他对象)是这样一个Lambda表达式,它的结果依赖于流管道执行期间可能会改变状态。有状态的lambda的一个例子是map()的参数:

 Set<Integer> seen = Collections.synchronizedSet(new HashSet<>());
 stream.parallel().map(e -> { if (seen.add(e)) return 0; else return e; })...

这里,如果并行执行映射操作,由于线程调度的差异,相同输入的结果可能因运行而异,而使用无状态的lambda表达式,结果总是相同的。

还要注意,尝试从行为参数访问可变状态会给您带来安全性和性能方面的问题;如果您不同步对该状态的访问,则将导致数据争用,因此您的代码成为bad code,但是如果您确实同步对该状态的访问,则要面临破坏有益并行性的风险。最好的方法是避免使用有状态的行为参数来做完全流式处理操作。通常有一种方法可以重组流管道以避免状态化。

4、副作用

通常,不要鼓励行为参数对流操作的副作用,因为它们经常会导致无意识地违反无状态要求以及其他线程安全危害。

如果行为参数确实具有副作用,除非明确说明,否则不能保证 这些副作用对于其他线程的 可见性,也不能保证在同一流管道中对“相同”元素的不同操作在同一线程中执行。此外,这些效果的排序可能令人惊讶。即使将管道约束为产生与流源的遇到顺序一致的 结果(例如,IntStream.range(0,5).parallel().map(x -> x*2).toArray() 必须产生[0, 2, 4, 6, 8]),也无法保证将mapper函数应用于单个元素的顺序或给指定元素执行任何行为参数的线程。

许多可能会尝试使用副作用的计算都可以更安全,更有效地表达而没有副作用,例如使用 reduction 代替可变的累加器。但是,诸如println()用于调试目的的副作用通常是无害的。少量的流操作(例如 forEach()和)peek()只能通过副作用进行操作;这些应谨慎使用。

作为如何将不正确使用副作用的流管道转换为不使用副作用的流管道的示例,以下代码在字符串流中搜索与给定正则表达式匹配的字符串,并将匹配项放入列表中。

ArrayList<String> results = new ArrayList<>();
stream.filter(s -> pattern.matcher(s).matches())
   .forEach(s -> results.add(s));  // Unnecessary use of side-effects!

此代码不必要地使用副作用。 如果并行执行, ArrayList的非线程安全性将导致不正确的结果,并且添加所需的同步将导致争用,从而破坏并行性的好处。 此外,这里使用副作用是完全不必要的; forEach()可以简单地替换为更安全,更高效,更适合并行化的还原操作:

List<String>results =
	stream.filter(s -> pattern.matcher(s).matches())
		.collect(Collectors.toList());  // No side-effects!

5、排序

流可能有也可能没有被定义的遇到顺序。流是否有遇到顺序取决于源和中间操作。某些流源(例如List或数组)在本质上是有序的,而其他流源(例如HashSet)则不是。某些中间操作(例如sorted())可能会将遇到顺序强加给原本无序的流,而其他一些操作可能会使有序流变得无序(如BaseStream.unordered())。此外,某些终端操作可能会忽略遇到顺序,例如 forEach()。

如果对流进行了排序,则大多数操作将被约束为按其遇到顺序对元素进行操作;如果流的源是List 包含的[1, 2, 3],则执行的结果map(x -> x*2) 必须是[2, 4, 6]。但是,如果源没有定义的遇到顺序,则值的任何排列都是[2, 4, 6]有效的结果。

对于顺序流,遇到顺序的存在或不存在不影响性能,仅影响确定性。 如果流被排序,则在相同的源上重复执行相同的流管道将产生相同的结果;如果没有排序,重复执行可能会产生不同的结果。

对于并行流,放宽排序约束有时可以使执行效率更高。如果元素的顺序无关紧要,则某些聚合操作(例如过滤重复项(distinct())或分组归约(Collectors.groupingBy()))可以更有效地实现。类似地,本质上与遇到顺序相关的操作(例如limit())可能需要缓冲以确保正确的顺序,从而削弱了并行性的优势。在流具有遇到顺序的情况下,但用户并不特别关心该遇到顺序,请使用unordered()可能会提高某些有状态或终端操作的并行性能。但是,大多数流管道,例如上面的“widgets的weight的总和”示例,即使在排序约束下也仍然有效地并行化。

六、Reduction操作

归约(reduction)操作

归约(reduction)操作(也称为折叠fold)通过重复应用组合操作(如查找一组数字的和或最大值,或将元素累加到一个列表中)来获取一系列输入元素并将它们组合成一个汇总结果。streams类有多种形式的常规归约操作,称为reduce()和collect(),还有多种专门的归约形式,如sum()、max()或count()。

当然,这些操作可以很容易地实现为简单的顺序循环,如下所示:

int sum = 0;
for (int x : numbers) {
	sum += x;
}

使用reduce运算的原因

但是,有充分的理由倾向于使用reduce运算,而不是像上面这样的累加。reduction不仅是“更抽象的”(它在整个流上而不是在单个元素上运行),而且,只要用于处理元素的function(s)是关联性的(Associativity)和无状态的,正确构造的reduce操作就具有固有的可并行性。例如,给定一个我们想要求和的数字流,我们可以这样写:

int sum = numbers.stream().reduce(0, (x,y) -> x+y);

或者:

 int sum = numbers.stream().reduce(0, Integer::sum);

这些还原操作几乎无需修改即可并行安全地运行:

int sum = numbers.parallelStream().reduce(0, Integer::sum);

Reduction并行化效果很好,因为该实现可以并行处理数据的子集,然后组合中间结果以获得最终的正确答案。(即使该语言具有“并行的for-each”构造,变异累积方法仍将要求开发人员为共享的累积变量提供线程安全的更新sum,然后所需的同步可能会消除并行性带来的任何性能提升。 )使用reduce()可以消除并行化归约操作的所有负担,并且该库可以提供高效的并行实现,而无需其他同步。

例子

前面显示的“widgets”示例显示了reduction 操作如何与其他操作结合使用,以批量操作代替循环。如果widgets 是具有getWeight方法的Widget对象的集合,我们可以使用以下方法找到最重的Widget:

OptionalInt heaviest = widgets.parallelStream()
		.mapToInt(Widget::getWeight)
		.max();

在更一般的形式上, 类型<T>的元素的reduce 操作产生类型<U>的结果需要三个参数:

<U> U reduce(U identity,
    BiFunction<U, ? super T, U> accumulator,
    BinaryOperator<U> combiner);

这里, 标识元素既是reduction的初始种子值,也是没有输入元素的默认结果。accumulator函数接受部分结果和下一个元素,并产生一个新的部分结果。combiner函数结合了两个部分结果产生一个新的部分结果。(在并行reductions中,combiner是必需的,其中对输入进行分区,为每个分区进行部分累加计算,然后将部分结果合并以产生最终结果。)

更正式地, 此identity值必须是combiner函数的标识 。这意味着对于所有的U,combiner.apply(identity, u) 等于【equals()】U。此外, combiner函数必须是associative ,必须与该accumulator功能兼容:对于所有u 和t,combiner.apply(u, accumulator.apply(identity, t))必须与accumulator.apply(u, t)相等【equals()】。

三参数形式是双参数形式的泛化,将映射步骤合并到累加步骤中。们可以使用更一般的形式重构“widgets的weight的总和”示例,如下所示:

int sumOfWeights = widgets.stream()
	.reduce(0,
	(sum, b) -> sum + b.getWeight())
	Integer::sum);

尽管显式的map-reduce形式更具可读性,因此通常应首选。对于可以通过组合map和reduce为单个功能来优化重要工作的情况,可以提供通用形式。

1、可变的reduction

一个可变reduction操作累积输入元素到一个可变的结果的容器(如Collection或StringBuilder),作为其处理流中的元素。

如果我们想获取字符串流并将其连接为单个长字符串,则可以通过普通reduction来实现:

String concatenated = strings.reduce("", String::concat)

我们将获得理想的结果,甚至以并行的方式。但是,我们可能对性能不满意!这样的实现将进行大量的字符串复制,并且运行时间的字符数将为O(n ^ 2)。一种更有效的方法是将结果累积到中StringBuilder,这是用于累积字符串的可变容器。就像普通reduction那样,我们可以使用相同的技术来使可变reduction并行化。

可变reduction操作collect()

可变reductio操作称为collect() ,因为它将所需的结果收集到结果容器(如Collection)中。一个collect操作需要三个函数:Supplier函数,用于构造结果容器的新实例;累加器函数,用于将输入元素合并到结果容器中;以及合并功能,用于将一个结果容器的内容合并到另一个结果容器中。这种形式与普通reduction的一般形式非常相似:

<R> R collect(Supplier<R> supplier,
	BiConsumer<R, ? super T> accumulator,
	BiConsumer<R, R> combiner);

抽象的方式表达collect的好处

同reduce()一样,以抽象的方式表达collect的好处是可以直接适应并行化:只要累加和合并函数满足适当的要求,我们就可以并行累加部分结果,然后将它们组合起来。

例如,要将流中String表示形式的元素收集到中ArrayList,我们可以编写明显的顺序for-each形式:

ArrayList<String> strings = new ArrayList<>();
for (T element : stream) {
	strings.add(element.toString());
}

或者我们可以使用可并行化的收集形式:

ArrayList<String> strings = stream.collect(() -> new ArrayList<>(),
		(c, e) -> c.add(e.toString()),
		(c1, c2) -> c1.addAll(c2));

或者,将映射操作从累加器函数中拉出,我们可以更简洁地表示为:

List<String> strings = stream.map(Object::toString)
	.collect(ArrayList::new, 
		ArrayList::add, 
		ArrayList::addAll);

在这里,我们的Supplier就是ArrayList 构造函数,累加器将字符串化的元素添加到中 ArrayList,组合器只需使用addAll将字符串从一个容器复制到另一个容器中。

Collector的使用

collect()的Supplier、累加器 和 合并器这三方面紧密相连,我们可以使用 Collector 的抽象来捕获这三个方面。上面的将字符串收集到List中的示例可以使用标准Collector进行重写,如下:

List<String> strings = stream.map(Object::toString)
			.collect(Collectors.toList());

将可变reduction包装到Collector 中有另一个优点:可组合性(composability)。类Collectors包含许多用于收集器的预定义工厂,包括将一个收集器转换为另一个收集器的组合器。例如,假设我们有一个收集器来计算Employee流的工资总和,如下所示:

Collector<Employee, ?, Integer> summingSalaries
	= Collectors.summingInt(Employee::getSalary);

(?用于Collector的第二个类型参数仅表示我们不关心此收集器使用的中间表示形式。)如果我们要创建一个收集器以按部门列出工资总额,则可以使用以下summingSalaries方法 复用groupingBy

Map<Department, Integer> salariesByDept = 
employees.stream().collect(
	Collectors.groupingBy(Employee::getDepartment,
	summingSalaries));

collect()并行化条件

与常规 reduction 操作一样,collect()只有在满足适当条件的情况下,操作才能并行化。对于任何部分累加的结果,将其与空的结果容器组合必须产生等效的结果。也就是说,对于部分累加结果p( 它是任何累加器和组合器调用序列的结果),p必须等价 combiner.apply(p, supplier.get()):

A a1 = supplier.get();
accumulator.accept(a1, t1);
accumulator.accept(a1, t2);
R r1 = finisher.apply(a1);  // result without splitting

A a2 = supplier.get();
accumulator.accept(a2, t1);
A a3 = supplier.get();
accumulator.accept(a3, t2);
R r2 = finisher.apply(combiner.apply(a2, a3));  // result with splitting

在此,等价通常表示Object.equals(Object)。但在某些情况下,可以放宽等效性以考虑顺序差异。

2、Reduction、并发 和 排序

并发reduction 

对于一些复杂的减少操作,例如一个collect() ,其产生Map ,如:

Map<Buyer, List<Transaction>> salesByBuyer
	= txns.parallelStream()
		.collect(Collectors.groupingBy(Transaction::getBuyer));

并行执行该操作实际上可能适得其反。这是因为对于某些Map实现而言,合并步骤(通过键将一个Map合并到另一个Map)可能会很昂贵。

但是,假设此 reduction 中使用的结果容器是可并发地修改的集合(如ConcurrentHashMap) 。在这种情况下,累加器的并行调用实际上可以将它们的结果同时存入相同的共享结果容器中,从而无需组合器来合并不同的结果容器。这可能会提高并行执行性能。 我们称之为并发reduction 。

Collector.Characteristics.CONCURRENT特性标记

支持并发 reduction 的CollectorCollector.Characteristics.CONCURRENT特性标记。然而,并发聚集(concurrent collection)也有缺点。如果有多个线程将结果同时存储到共享容器中,则结果存储的顺序是不确定的。因此,只有对于正在处理的流的顺序不重要,并发 reduction  才是可能的。该Stream.collect(Collector) 实现仅在以下情况下执行并发reduction:

您可以使用BaseStream.unordered()方法确保流是无序的 。例如:

Map<Buyer, List<Transaction>> salesByBuyer
	= txns.parallelStream()
		.unordered()
		.collect(groupingByConcurrent(Transaction::getBuyer));

(其中Collectors.groupingByConcurrent(java.util.function.Function<? super T, ? extends K>)是并发等效于groupingBy )。

请注意,如果给定键的元素必须以源中出现的顺序出现,则我们不能使用并发reduction,因为并发插入会破坏排序。然后,我们将被限制为执行顺序reduction或基于合并的并发reduction。

3、Associativity

如果满足以下条件,则 操作符或函数op是关联的:

(a op b) op c == a op (b op c)

如果将其扩展为四个术语,可以看出这一点对并行评估的重要性:

a op b op c op d == (a op b) op (c op d)

因此,我们可以用(c op d)进行对(a op b)并行评估,然后调用op结果。

关联操作的示例包括数字加法、取最小值、取最大值以及字符串连接。

七、低级stream构造

到目前为止,所有Stream示例都使用了类似 Collection.stream()Arrays.stream(Object[]) 获得流的方法。这些Stream承载方法是如何实现的?

类StreamSupport和Spliterator

StreamSupport具有许多用于创建流的低级方法,所有方法都使用某种形式的SpliteratorSpliterator 是迭代器的并行模拟;它描述了(可能是无限的)元素集合,支持顺序前进,批量遍历,并将输入的一部分分解成可并行处理的另一个spliterator。在最低级别上,所有流均由spliterator驱动。

在实现spliterator方面有许多实现选择,几乎所有这些都是实现简单性和使用spliterator的流的运行时性能之间的权衡。创建spliterator最简单但性能最低的方法是,使用Spliterators.spliteratorUnknownSize(java.util.Iterator, int)从迭代器创建spliterator。虽然这样的spliterator 可以使用,但由于丢失了大小的信息(底层数据集有多大)以及被限制在简单的分割算法上,所以可能会提供较差的并行性能。

更高质量的spliterator将提供均衡的和已知的尺寸拆分,准确大小的信息,以及很多其他spliterator 的 characteristics 或数据,它们可由实现用于优化执行。

用于可变数据源的Spliterator还有一个挑战。绑定数据的时间,因为数据可能会在创建spliterator 的时间和执行流管道的时间之间发生变化。理想情况下,流的spliterator 应报告IMMUTABLE或 CONCURRENT的特性。如果不是,则应延迟绑定。如果源无法直接提供推荐的Spliterator,则可以使用Supplier间接提供spliterator,并通过Supplier接受的stream()版本构造Stream。只有在流量管道的终端运行开始之后,才能从供应商获得分配器。

这些要求显着减小了在Stream源的突变与Stram管道执行之间的潜在干扰范围。基于具有所需特性的spliterator的Steam,或使用基于Supplier的工厂表单的Steam,在终端操作开始之前不受数据源修改的影响(前提是Steam操作的行为参数符合非干扰和无状态)

文章来源

来自:https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值