T
- 流元素的类型
public interface Stream<T> extends BaseStream<T,Stream<T>>
Stream
和IntStream
的汇总操作 :
int sum = widgets.stream() .filter(w -> w.getColor() == RED) .mapToInt(w -> w.getWeight()) .sum();
在这个例子中, widgets
是Collection<Widget>
。
我们通过Collection.stream()
创建一个Widget
对象的流,过滤它以产生仅包含红色小部件的流,然后将其转换为表示每个红色小部件的权重的int
值。
然后将该流相加以产生总重量。
除了Stream
,其为对象引用的流,存在原语特为IntStream
, LongStream
和DoubleStream
,所有这些都称为“流”和符合此处描述的特征和限制。
为了执行计算,流operations被组合成流管道 。 流管线由源(其可以是阵列,集合,生成函数,I / O通道等)组成,零个或多个中间操作 (其将流转换成另一个流,例如filter(Predicate)
)以及终端操作 (产生结果或副作用,如count()
或forEach(Consumer)
)。 流懒惰 源数据上的计算仅在终端操作启动时执行,源元素仅在需要时才被使用。
集合和流动,同时具有一些表面上的相似之处,具有不同的目标。 集合主要关注其元素的有效管理和访问。 相比之下,流不提供直接访问或操纵其元素的手段,而是关心声明性地描述其源和将在该源上进行聚合的计算操作。 但是,如果提供的流操作不提供所需的功能,则可以使用BaseStream.iterator()
和BaseStream.spliterator()
操作来执行受控遍历。
流管道,如上面的“小部件”示例,可以被视为流源上的查询 。 除非源是明确设计用于并发修改(例如ConcurrentHashMap
),否则在查询流源时可能会导致不可预测或错误的行为。
大多数流操作接受描述用户指定行为的参数,例如上面示例中传递给mapToInt
的lambda表达式w -> w.getWeight()
。 为了保持正确的行为,这些行为参数 :
这些参数始终是functional interface的实例 ,例如Function
,并且通常是lambda表达式或方法引用。 除非另有说明,否则这些参数必须为非空值 。
流只能运行(调用中间或终端流操作)一次。 这排除了例如“分叉”流,其中相同的源提供两条或多条流水线,或同一流的多遍。 如果流实现可能会丢失IllegalStateException
,如果它检测到该流被重用。 然而,由于一些流操作可能返回其接收器而不是新的流对象,所以在所有情况下可能无法检测到重用。
Streams有一个BaseStream.close()
方法和实现AutoCloseable
,但几乎所有的流实例实际上不需要在使用后关闭。 一般来说,只有来源为IO通道的流(如Files.lines(Path, Charset)
返回的流 )才需要关闭。 大多数流都由集合,数组或生成函数支持,这些函数不需要特殊的资源管理。 (如果流确实需要关闭,则可以在try
-with-resources语句中将其声明为资源。)
流管线可以顺序执行,也可以在parallel中执行。 此执行模式是流的属性。 流被创建为具有顺序或并行执行的初始选择。 (例如, Collection.stream()
创建一个顺序流,并且Collection.parallelStream()
创建一个并行的)。执行模式的选择可以由BaseStream.sequential()
或BaseStream.parallel()
方法修改,并且可以使用BaseStream.isParallel()
方法进行查询。
IntStream
, LongStream
, DoubleStream
, java.util.stream
Modifier and Type | Interface and Description |
---|---|
static interface |
Stream.Builder<T>
Stream 可变构建器。
|
Modifier and Type | Method and Description |
---|---|
boolean |
allMatch(Predicate<? super T> predicate)
返回此流的所有元素是否与提供的谓词匹配。
|
boolean |
anyMatch(Predicate<? super T> predicate)
返回此流的任何元素是否与提供的谓词匹配。
|
static <T> Stream.Builder<T> |
builder()
返回一个
Stream 的构建器。
|
<R,A> R |
collect(Collector<? super T,A,R> collector)
|
<R> R |
collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)
对此流的元素执行
mutable reduction操作。
|
static <T> Stream<T> |
concat(Stream<? extends T> a, Stream<? extends T> b)
创建一个懒惰连接的流,其元素是第一个流的所有元素,后跟第二个流的所有元素。
|
long |
count()
返回此流中的元素数。
|
Stream<T> |
distinct()
返回由该流的不同元素(根据
Object.equals(Object) )组成的流。
|
static <T> Stream<T> |
empty()
返回一个空的顺序
Stream 。
|
Stream<T> |
filter(Predicate<? super T> predicate)
返回由与此给定谓词匹配的此流的元素组成的流。
|
Optional<T> |
findAny()
返回描述流的一些元素的 Optional 如果流为空,则返回一个空的Optional 。
|
Optional<T> |
findFirst()
返回描述此流的第一个元素的 Optional 如果流为空,则返回一个空的Optional 。
|
<R> Stream<R> |
flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
返回由通过将提供的映射函数应用于每个元素而产生的映射流的内容来替换该流的每个元素的结果的流。
|
DoubleStream |
flatMapToDouble(Function<? super T,? extends DoubleStream> mapper)
返回一个
DoubleStream ,其中包含将该流的每个元素替换为通过将提供的映射函数应用于每个元素而产生的映射流的内容的结果。
|
IntStream |
flatMapToInt(Function<? super T,? extends IntStream> mapper)
返回一个
IntStream ,其中包含将该流的每个元素替换为通过将提供的映射函数应用于每个元素而产生的映射流的内容的结果。
|
LongStream |
flatMapToLong(Function<? super T,? extends LongStream> mapper)
返回一个
LongStream ,其中包含将该流的每个元素替换为通过将提供的映射函数应用于每个元素而产生的映射流的内容的结果。
|
void |
forEach(Consumer<? super T> action)
对此流的每个元素执行操作。
|
void |
forEachOrdered(Consumer<? super T> action)
如果流具有定义的遇到顺序,则以流的遇到顺序对该流的每个元素执行操作。
|
static <T> Stream<T> |
generate(Supplier<T> s)
返回无限顺序无序流,其中每个元素由提供的
Supplier 。
|
static <T> Stream<T> |
iterate(T seed, UnaryOperator<T> f)
返回有序无限连续
Stream 由函数的迭代应用产生
f 至初始元素
seed ,产生
Stream 包括
seed ,
f(seed) ,
f(f(seed)) ,等
|
Stream<T> |
limit(long maxSize)
返回由此流的元素组成的流,截短长度不能超过
maxSize 。
|
<R> Stream<R> |
map(Function<? super T,? extends R> mapper)
返回由给定函数应用于此流的元素的结果组成的流。
|
DoubleStream |
mapToDouble(ToDoubleFunction<? super T> mapper)
返回一个
DoubleStream ,其中包含将给定函数应用于此流的元素的结果。
|
IntStream |
mapToInt(ToIntFunction<? super T> mapper)
返回一个
IntStream ,其中包含将给定函数应用于此流的元素的结果。
|
LongStream |
mapToLong(ToLongFunction<? super T> mapper)
返回一个
LongStream ,其中包含将给定函数应用于此流的元素的结果。
|
Optional<T> |
max(Comparator<? super T> comparator)
根据提供的
Comparator 返回此流的最大元素。
|
Optional<T> |
min(Comparator<? super T> comparator)
根据提供的
Comparator 返回此流的最小元素。
|
boolean |
noneMatch(Predicate<? super T> predicate)
返回此流的元素是否与提供的谓词匹配。
|
static <T> Stream<T> |
of(T... values)
返回其元素是指定值的顺序排序流。
|
static <T> Stream<T> |
of(T t)
返回包含单个元素的顺序
Stream 。
|
Stream<T> |
peek(Consumer<? super T> action)
返回由该流的元素组成的流,另外在从生成的流中消耗元素时对每个元素执行提供的操作。
|
Optional<T> |
reduce(BinaryOperator<T> accumulator)
|
T |
reduce(T identity, BinaryOperator<T> accumulator)
使用提供的身份值和
associative累积功能对此流的元素执行
reduction ,并返回减小的值。
|
<U> U |
reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
执行
reduction在此流中的元素,使用所提供的身份,积累和组合功能。
|
Stream<T> |
skip(long n)
在丢弃流的第一个
n 元素后,返回由该流的
n 元素组成的流。
|
Stream<T> |
sorted()
返回由此流的元素组成的流,根据自然顺序排序。
|
Stream<T> |
sorted(Comparator<? super T> comparator)
返回由该流的元素组成的流,根据提供的
Comparator 进行排序。
|
Object[] |
toArray()
返回一个包含此流的元素的数组。
|
<A> A[] |
toArray(IntFunction<A[]> generator)
使用提供的
generator 函数返回一个包含此流的元素的数组,以分配返回的数组,以及分区执行或调整大小可能需要的任何其他数组。
|
close, isParallel, iterator, onClose, parallel, sequential, spliterator, unordered
Stream<T> filter(Predicate<? super T> predicate)
这是一个intermediate operation 。
predicate
-一个
non-interfering ,
stateless谓词应用到每个元素,以确定是否它应包含
<R> Stream<R> map(Function<? super T,? extends R> mapper)
这是一个intermediate operation 。
R
- 新流的元素类型
mapper
-一个
non-interfering ,
stateless函数应用到每个元件
IntStream mapToInt(ToIntFunction<? super T> mapper)
IntStream
,其中包含将给定函数应用于此流的元素的结果。
这是一个intermediate operation 。
mapper
-一个
non-interfering ,
stateless函数应用到每个元件
LongStream mapToLong(ToLongFunction<? super T> mapper)
LongStream
,其中包含将给定函数应用于此流的元素的结果。
这是一个intermediate operation 。
mapper
-一个
non-interfering ,
stateless函数应用到每个元件
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)
DoubleStream
,其中包含将给定函数应用于此流的元素的结果。
这是一个intermediate operation 。
mapper
-一个
non-interfering ,
stateless函数应用到每个元件
<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
closed
。
(如果映射的流是null
则使用空的流)。
这是一个intermediate operation 。
flatMap()
操作具有对流的元素应用一对多变换,然后将所得到的元素平坦化为新流的效果。
例子。
如果orders
是采购订单流,并且每个采购订单都包含订单项的集合,则以下生成包含所有订单中所有订单项的流:
orders.flatMap(order -> order.getLineItems().stream())...
如果path
是路径到一个文件,那么下面产生的气流words
包含在该文件中:
Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8); Stream<String> words = lines.flatMap(line -> Stream.of(line.split(" +")));
传递给flatMap
的mapper
函数将一行(使用简单的正则表达式)分割成一个单词数组,然后从该数组中创建一个单词。
R
- 新流的元素类型
mapper
-一个
non-interfering ,
stateless功能应用到其产生新的值的流的每个元素
IntStream flatMapToInt(Function<? super T,? extends IntStream> mapper)
IntStream
,其中包含将该流的每个元素替换为通过将提供的映射函数应用于每个元素而产生的映射流的内容的结果。
每个映射的流在其内容被放入此流之后是closed
。
(如果映射的流为null
则使用空的流。)
这是一个intermediate operation 。
mapper
-一个
non-interfering ,
stateless功能应用到其产生新的值的流的每个元素
flatMap(Function)
LongStream flatMapToLong(Function<? super T,? extends LongStream> mapper)
LongStream
,其中包含将该流的每个元素替换为通过将提供的映射函数应用于每个元素而产生的映射流的内容的结果。
每个映射的流在其内容被放入此流之后为closed
。
(如果映射的流为null
则使用空的流。)
这是一个intermediate operation 。
mapper
-一个
non-interfering ,
stateless功能应用到其产生新的值的流的每个元素
flatMap(Function)
DoubleStream flatMapToDouble(Function<? super T,? extends DoubleStream> mapper)
DoubleStream
,其中包含将该流的每个元素替换为通过将提供的映射函数应用于每个元素而产生的映射流的内容的结果。
每个映射的流是closed
,其内容已经放入此流中。
(如果映射的流是null
则使用空的流。)
这是一个intermediate operation 。
mapper
-一个
non-interfering ,
stateless功能应用到其产生新的值的流的每个元素
flatMap(Function)
Stream<T> distinct()
Object.equals(Object)
)组成的流。
对于有序流,选择不同的元素是稳定的(对于重复的元素,首先在遇到顺序中出现的元素被保留。)对于无序流,不能保证稳定性。
distinct()
在并行管线是相对昂贵的(要求操作充当一个完整屏障,具有大量缓冲的开销)通常不需要的,和稳定性。
使用无序流源(如generate(Supplier)
)或具有除去排序约束BaseStream.unordered()
可导致显著更高效的执行为distinct()
在并行管线,如果情况许可的语义。
如果需要与遇到顺序一致, distinct()
在并行流水线中使用distinct()
您的性能或内存利用率不佳,则使用BaseStream.sequential()
切换到顺序执行可能会提高性能。
Stream<T> sorted()
Comparable
,一个java.lang.ClassCastException
执行终端操作时,可以抛出。
对于有序流,排序稳定。 对于无序的流,不能保证稳定性。
Stream<T> sorted(Comparator<? super T> comparator)
comparator
-一个
non-interfering ,
stateless
Comparator
被用于比较流元素
Stream<T> peek(Consumer<? super T> action)
这是一个intermediate operation 。
对于并行流管线,可以在上游操作的任何时间和任何线程中调用该元素可用的动作。 如果操作修改共享状态,则负责提供所需的同步。
Stream.of("one", "two", "three", "four") .filter(e -> e.length() > 3) .peek(e -> System.out.println("Filtered value: " + e)) .map(String::toUpperCase) .peek(e -> System.out.println("Mapped value: " + e)) .collect(Collectors.toList());
action
- 一个
88466851784848要从流中消耗的元素上执行的操作
Stream<T> limit(long maxSize)
maxSize
。
limit()
通常是在连续的流管道的廉价的操作,它可以是订购的并行管线相当昂贵的,特别是对于大的值maxSize
,由于limit(n)
被约束返回不是任何n个元素,但在遭遇顺序中的第n个元素。
使用无序流源(如generate(Supplier)
)或去除所述排序约束与BaseStream.unordered()
可导致显著加速比limit()
在并行管线,如果情况许可的语义。
如果需要与遇到顺序一致, limit()
在并行流水线中遇到limit()的性能下降或内存利用率下降,则使用BaseStream.sequential()
切换到顺序执行可能会提高性能。
maxSize
- 流应该限制的元素数量
IllegalArgumentException
- 如果
maxSize
为负数
Stream<T> skip(long n)
skip()
通常是在连续的流管道的廉价的操作,它可以是订购的并行管线相当昂贵的,特别是对于大的值n
,由于skip(n)
被约束为跳过不是任何n个元素,但在遭遇顺序中的第n个元素。
使用无序流源(如generate(Supplier)
)或去除所述排序约束与BaseStream.unordered()
可导致显著加速比skip()
在并行管线,如果情况许可的语义。
如果具有遭遇顺序的一致性是必须的,并且遇到性能或存储器利用率差与skip()
在并行管线,在切换到的顺序执行与BaseStream.sequential()
可以提高性能。
n
- 要跳过的主要元素的数量
IllegalArgumentException
- 如果
n
为负数
void forEach(Consumer<? super T> action)
这是一个terminal operation 。
这个操作的行为显然是不确定的。 对于并行流管道,此操作不保证遵守流的遇到顺序,因为这样做将牺牲并行性的好处。 对于任何给定的元素,动作可以在图书馆选择的任何时间和任何线索中执行。 如果操作访问共享状态,则负责提供所需的同步。
action
- 一个
non-interfering对元素执行的动作
void forEachOrdered(Consumer<? super T> action)
这是一个terminal operation 。
此操作一次处理元素,如果存在,则按照遇到的顺序进行处理。 执行一个元素happens-before执行后续元素的操作的操作,但对于任何给定的元素,该操作可以在库选择的任何线程中执行。
action
- 一个
non-interfering对元素执行的动作
forEach(Consumer)
Object[] toArray()
这是一个terminal operation 。
<A> A[] toArray(IntFunction<A[]> generator)
generator
函数返回包含此流的元素的数组,以分配返回的数组,以及分区执行或调整大小可能需要的任何其他数组。
这是一个terminal operation 。
Person[] men = people.stream() .filter(p -> p.getGender() == MALE) .toArray(Person[]::new);
A
- 结果数组的元素类型
generator
- 产生所需类型和所提供长度的新数组的函数
ArrayStoreException
- 如果从数组生成器返回的数组的运行时类型不是此流中每个元素的运行时类型的超类型
T reduce(T identity, BinaryOperator<T> accumulator)
T result = identity; for (T element : this stream) result = accumulator.apply(result, element) return result;
但不限于顺序执行。
identity
值必须是累加器函数的标识。 这意味着,对于所有t
, accumulator.apply(identity, t)
等于t
。 accumulator
功能必须是associative功能。
这是一个terminal operation 。
Integer sum = integers.reduce(0, (a, b) -> a+b);
要么:
Integer sum = integers.reduce(0, Integer::sum);
尽管与简单地将循环中的总体变量相比,这可能看起来更为迂回的方式进行,但减少操作更平稳地并行化,而不需要额外的同步,并大大降低了数据竞争的风险。
identity
- 累积函数的身份值
accumulator
-一个
associative ,
non-interfering ,
stateless功能组合两个值
Optional<T> reduce(BinaryOperator<T> accumulator)
Optional
。
这相当于:
boolean foundAny = false; T result = null; for (T element : this stream) { if (!foundAny) { foundAny = true; result = element; } else result = accumulator.apply(result, element); } return foundAny ? Optional.of(result) : Optional.empty();
但不限于顺序执行。
accumulator
功能必须是associative功能。
这是一个terminal operation 。
accumulator
-一个
associative ,
non-interfering ,
stateless功能组合两个值
Optional
描述了减少的结果
NullPointerException
- 如果减少的结果为空
reduce(Object, BinaryOperator)
,
min(Comparator)
,
max(Comparator)
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
U result = identity; for (T element : this stream) result = accumulator.apply(result, element) return result;
但不限于顺序执行。
identity
值必须是组合器功能的标识。 这意味着,对于所有u
, combiner(identity, u)
等于u
。 另外combiner
功能必须兼容accumulator
功能; 对于所有u
和t
,以下必须保持:
combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)
这是一个terminal operation 。
map
和reduce
操作的明确组合,可以更简单地表示使用此表单的许多裁减。
accumulator
函数充当融合映射器和累加器,这有时比单独的映射和还原更有效,例如当知道先前减少的值允许您避免某些计算时。
U
- 结果的类型
identity
- 组合器功能的标识值
accumulator
-一个
associative ,
non-interfering ,
stateless功能用于将一个额外的元件到结果
combiner
-一个
associative ,
non-interfering ,
stateless功能用于组合两个值,它必须与蓄能器功能兼容
reduce(BinaryOperator)
,
reduce(Object, BinaryOperator)
<R> R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)
ArrayList
,并且通过更新结果的状态而不是通过替换结果来合并元素。
这产生的结果相当于:
R result = supplier.get(); for (T element : this stream) accumulator.accept(result, element); return result;
像reduce(Object, BinaryOperator)
一样 , collect
操作可以并行化,而不需要额外的同步。
这是一个terminal operation 。
collect()
。
例如,以下将会将字符串累加到一个ArrayList
:
List<String> asList = stringStream.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
以下将使用一串字符串并将它们连接成一个字符串:
String concat = stringStream.collect(StringBuilder::new, StringBuilder::append, StringBuilder::append) .toString();
R
- 结果的类型
supplier
- 创建新结果容器的函数。
对于并行执行,可以多次调用此函数,并且每次都必须返回一个新的值。
accumulator
-一个
associative ,
non-interfering ,
stateless功能用于将一个额外的元件到结果
combiner
-一个
associative ,
non-interfering ,
stateless功能用于组合两个值,它必须与蓄能器功能兼容
<R,A> R collect(Collector<? super T,A,R> collector)
Collector
。
A Collector
将用作参数的函数封装到collect(Supplier, BiConsumer, BiConsumer)
,允许重用集合策略和组合收集操作(如多级分组或分区)。
如果流是并行的,并且Collector
是concurrent
,并且流是无序的或收集器是unordered
,则将执行并发的减少(有关并发减少的细节,请参阅Collector
)。
这是一个terminal operation 。
当并行执行时,可以实例化,填充和合并多个中间结果,以便保持可变数据结构的隔离。 因此,即使与非线程安全的数据结构(例如ArrayList
)并行执行,并行还原也不需要额外的同步。
List<String> asList = stringStream.collect(Collectors.toList());
以下将按城市分类Person
对象:
Map<String, List<Person>> peopleByCity = personStream.collect(Collectors.groupingBy(Person::getCity));
以下将按国家和城市对Person
对象进行分类,将两个Collector
组合在一起:
Map<String, Map<String, List<Person>>> peopleByStateAndCity = personStream.collect(Collectors.groupingBy(Person::getState, Collectors.groupingBy(Person::getCity)));
R
- 结果的类型
A
- 中间累积类型的
Collector
collector
-
Collector
减少的Collector
collect(Supplier, BiConsumer, BiConsumer)
, Collectors
Optional<T> min(Comparator<? super T> comparator)
comparator
-一个
non-interfering ,
stateless
Comparator
比较该流的元素
Optional
此流的最小元素的
Optional
如果流为空,
Optional
NullPointerException
- 如果最小元素为空
Optional<T> max(Comparator<? super T> comparator)
comparator
-一个
non-interfering ,
stateless
Comparator
比较该流的元素
Optional
此流的最大元素的
Optional
如果流为空,
Optional
NullPointerException
- 如果最大元素为空
long count()
boolean anyMatch(Predicate<? super T> predicate)
false
返回和谓语不评估。
predicate
-一个
non-interfering ,
stateless谓词适用于该流的元素
true
如果流的任何元素匹配提供的谓词,否则为
false
boolean allMatch(Predicate<? super T> predicate)
true
返回和谓语不评估。
true
(不管的P(X))。
predicate
- a
non-interfering,stateless
谓词适用于此流的元素
true
如果流的所有元素匹配提供的谓词或流是空的,否则
false
boolean noneMatch(Predicate<? super T> predicate)
true
返回和谓语不评估。
true
,不论P(x)如何。
predicate
-一个
non-interfering ,
stateless谓词适用于该流的元素
true
如果流中没有元素匹配提供的谓词或流是空的,否则
false
Optional<T> findFirst()
Optional
如果流为空,则返回一个空的Optional
。
如果流没有遇到顺序,则可能会返回任何元素。
Optional
此流的第一个元素的
Optional
如果流为空,
Optional
NullPointerException
- 如果选择的元素为空
Optional<T> findAny()
Optional
如果流为空,则返回一个空的Optional
。
这是一个short-circuiting terminal operation 。
这个操作的行为显然是不确定的; 可以自由选择流中的任何元素。 这是为了允许并行操作中的最大性能; 成本是同一来源上的多次调用可能不会返回相同的结果。 (如果需要稳定的结果,请改用findFirst()
)
Optional
此流的某些元素的
Optional
如果流为空,
Optional
NullPointerException
- 如果选择的元素为空
findFirst()
static <T> Stream.Builder<T> builder()
Stream
的生成器。
T
- 元素的类型
static <T> Stream<T> empty()
Stream
。
T
- 流元素的类型
static <T> Stream<T> of(T t)
Stream
。
T
- 流元素的类型
t
- 单一元素
@SafeVarargs static <T> Stream<T> of(T... values)
T
- 流元素的类型
values
- 新流的元素
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
Stream
由函数的迭代应用产生f
至初始元素seed
,产生Stream
包括seed
, f(seed)
, f(f(seed))
,等
第一元件(位置0
在) Stream
将是提供seed
。 对于n > 0
,位置n
的元素将是将函数f
应用于位置n - 1
的元素的n - 1
。
T
- 流元素的类型
seed
- 初始元素
f
- 要应用于前一个元素以生成新元素的函数
Stream
static <T> Stream<T> generate(Supplier<T> s)
Supplier
。
这适合于产生恒定流,随机元素流等
T
- 流元素的类型
s
-所述
Supplier
生成的元素的
Stream
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
StackOverflowException
。
T
- 流元素的类型
a
- 第一个流
b
- 第二个流