Java8中Stream详解

1.什么是Stream

Stream中文称为,通过将集合转换为这么一种叫做"流"的元素序列,通过声明性方式,能够对集合中的每个元素进行一系列并行或串行的流水线操作

Stream是用函数式编程方式在集合类上进行复杂操作的工具。

2.创建Stream

2.1Stream接口的静态工厂方法

2.1.1of

of方法,其生成的Stream是有限长度的,Stream的长度为其内的元素个数。

方法 描述
of(T… values) 返回含有多个T元素的Stream
of(T t) 返回含有一个T元素的Stream

示例:

Stream<String> hello = Stream.of("hello", "world", "stream");

2.1.2iterate

iterate方法,其返回的是一个无限长度有序的Stream,其是通过函数f迭代应用于指定的元素种子seed,生成由seedf(seed),f(f(seed))以此类推等组成的Stream。

iterate(final T seed, final UnaryOperator<T> f)

示例:

Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(10);

上面示例,种子为0,可认为该Stream的第一个元素,通过f函数来产生第二个元素。接着,第二个元素,作为产生第三个元素的种子,从而产生了第三个元素,以此类推下去。需要注意的是,该Stream是无限长度的,应该使用filter、limit等来截取Stream,否则会一直循环下去。

2.1.3generator

generator方法,返回一个无限长度无序的Stream,其元素由Supplier接口提供。

generate(Supplier<T> s)

1.这种情形通常用于随机数、常量的 Stream,或者需要前后元素间维持着某种状态信息的Stream。

2.把Supplier实例传递给Stream.generate()生成的Stream,默认是串行(相对parallel而言)但无序的(相对ordered而言)。

示例:

Stream<Double> doubleStream = Stream.generate(Math::random).limit(10);

2.1.4empty

empty方法返回一个空的顺序Stream,该Stream里面不包含元素项。

示例:

Stream<String> emptyStream = Stream.empty();

2.1.5构建器builder()

builder方法返回一个Stream的构建器。

返回类型 方法 描述
Stream.Builder<T> builder() 返回Stream的构建器

Stream.Builder<T>

Stream的可变构建器。它允许通过逐一生成元素并将它们添加到Builder来创建Stream(没有使用ArrayList作为临时缓冲区的复制开销)
Stream流构建器具有生命周期,其从构建阶段开始,在该阶段期间可以添加元素,然后转换到内置阶段,之后可能不添加元素。内建阶段是在调用build()方法时开始的,它创建一个有序的Stream,其元素是添加到流构建器的元素,按添加顺序排列。

修饰符和类型 方法和描述
void accept(T t)向正在构建的流添加元素。
default Stream.Builder<T> add(T t)向正在构建的流添加元素。
Stream<T> build()构建流,将此构建器转换为内置/构建状态。

示例:

Stream.Builder<String> builder = Stream.builder();
builder.add("apple").add("banana");
builder.accept("orange");
builder.add("peach");
Stream<String> stringStream = builder.build();
stringStream.forEach(System.out::println);
// apple
// banana
// orange
// peach

2.2集合和数组

2.2.1集合

Collection接口的默认方法,把一个Collection对象转换成Stream

在java.util.Collection接口中定义了默认方法stream()parallelStream(),用来生成一个Stream。

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

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

示例:

List<String> list = Arrays.asList("hello", "world", "stream");
Stream<String> stream = list.stream();

2.2.2数组

在java.util.Arrays类中封装了一系列的stream()方法,不仅针对于任何类型的元素采用了泛型,更对于基本类型做了相应封装,以便提升Stream的处理效率。

数组中stream方法

示例:

String[] strings = {"hello", "world", "stream"};
Stream<String> stream = Arrays.stream(strings);

2.3其他

从文件读取流

示例:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

Path path = Paths.get("C:\\Users\\CreateStream.java");
Stream<String> streamFromFile = Files.lines(path);

Random.ints()
BufferedReader.lines()
BitSet.stream()
Pattern.splitAsStream(java.lang.CharSequence)
JarFile.stream()

3.操作分类

流的操作类型分为两种:

  • Intermediate:一个流可以后面跟随零个或多个intermediate操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
  • Terminal:一个流只能有一个terminal操作,当这个操作执行后,流就被使用"光"了,无法再被操作。所以这必定是流的最后一个操作。Terminal操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个side effect。

还有一种操作被称为short-circuiting。用以指:

  • 对于一个intermediate操作,如果它接受的是一个无限大(infinite/unbounded)的Stream,但返回一个有限的新Stream。
  • 对于一个terminal操作,如果它接受的是一个无限大的Stream,但能在有限的时间计算出结果。

当操作一个无限大的Stream,而又希望在有限时间内完成操作,则在管道内拥有一个short-circuiting操作是必要非充分条件。

序号 操作 类型 返回类型 参数
1 concat intermediate Stream<T> Stream<T>, Stream<T>
2 distinct stateful intermediate Stream<T>
3 filter intermediate Stream<T> Predicate<T>
4 map intermediate Stream<R> Function<T, R>
5 flatMap intermediate Stream<R> Function<T, R>
6 peek intermediate Stream<T> Consumer<T>
7 skip stateful intermediate Stream<T> long
8 sorted stateful intermediate Stream<T> [Comparator<T>]
9 parallel intermediate Stream<T>
10 sequential intermediate Stream<T>
11 unordered intermediate Stream<T>
12 forEach terminal void Consumer<T>
13 forEachOrdered terminal void Consumer<T>
14 toArray terminal Object[] [IntFunction<A[]>]
15 reduce terminal Optional<T>
T
U
BinaryOperator<T>
T, BinaryOperator<T>
U, BiFunction<U, T, U>, BinaryOperator<U>
16 collect terminal R Collector<T, A, R>
Supplier<R>, BiConsumer<R, T>, BiConsumer<R, R>
17 min terminal Optional<T> Comparator<T>
18 max terminal Optional<T> Comparator<T>
19 count terminal long
20 iterator terminal Stream<T> final T, final UnaryOperator<T>
21 limit short-circuiting stateful intermediate Stream<T> long
22 anyMatch short-circuiting intermediate boolean Predicate<T>
23 allMatch short-circuiting intermediate boolean Predicate<T>
24 noneMatch short-circuiting intermediate boolean Predicate<T>
25 findFirst short-circuiting intermediate Optional<T>
26 findAny short-circuiting intermediate Optional<T>

3.1中间操作

Intermediate主要是用来对Stream做出相应转换及限制流,实际上是将源Stream转换为一个新的Stream,以达到需求效果。

3.1.1concat

concat方法将两个Stream连接在一起,合成一个Stream。若两个输入的Stream都是排序的,则新Stream也是排序的;若输入的Stream中任何一个是并行的,则新的Stream也是并行的;若关闭新的Stream时,原两个输入的Stream都将执行关闭处理。

示例:

Stream<Integer> concatStream = Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5));

3.1.2distinct

distinct方法以达到去除掉原Stream中重复的元素,生成的新Stream中没有重复的元素。

For ordered streams, the selection of distinct elements is stable (for duplicated elements, the element appearing first in the encounter order is preserved.) For unordered streams, no stability guarantees are made.

对于有序流,不同元素的选择是稳定的(对于重复元素,将保留最先出现在遭遇顺序中的元素)。对于无序流,不提供稳定性保证。

示例:

Stream.of(1, 2, 3, 4, 1, 2, 3).distinct().forEach(System.out::println);
// 1
// 2
// 3
// 4

3.1.3filter

filter方法对原Stream按照指定条件过滤,在新建的Stream中,只包含满足条件的元素,将不满足条件的元素过滤掉。

示例:

Stream.of(1, 2, 3, 4).filter(item -> item % 2 == 0).forEach(System.out::println);
// 2
// 4

3.1.4map

map方法将对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。为了提高处理效率,官方已封装好了,三种变形:mapToDoublemapToIntmapToLong。如果想将原Stream中的数据类型,转换为double,int或者是long可以调用相对应的方法。

示例:

Stream.of(1, 2, 3).map(item -> "result:" + (item - 1)).forEach(System.out::println);
// result:0
// result:1
// result:2

3.1.5flatMap

flatMap方法与map方法类似,都是将原Stream中的每一个元素通过转换函数进行转换,不同的是,该换转函数的对象是一个Stream,也不会再创建一个新的Stream,而是将原Stream的元素取代为转换的Stream。如果转换函数生成的Stream为null,应由空Stream取代。flatMap有三个对于原始类型的变种方法,分别是:flatMapToDoubleflatMapToIntflatMapToLong

注意: flatMap操作的效果是对流的元素应用一对多的转换,然后将生成的元素扁平为新的流。

Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

示例1:

String[] words = {"Hello", "World"};
// [H, e, l, l, o],[W, o, r, l, d]
Stream<String[]> stream = Arrays.stream(words).map(w -> w.split(""));
Stream<String> stringStream = stream.flatMap(Arrays::stream);
stringStream.forEach(System.out::print); // HelloWorld

示例2:

Stream.of(1, 2, 3).flatMap(a -> Stream.of(a + 1)).forEach(System.out::println);
// 2
// 3
// 4

flatMap传入的Lambda表达式必须是Function实例,参数可以为任意类型,而其返回值类型必须是一个Stream。

3.1.6peek

peek方法生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer),新Stream每个元素被消费的时候都会执行给定的消费函数。

示例:

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());
// Filtered value: three
// Mapped value: THREE
// Filtered value: four
// Mapped value: FOUR

注意:

This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline.

此方法主要用于支持调试,当你想要在元素流过管道中的某个点时看到这些元素。

3.1.7skip

skip方法将过滤掉原Stream中的前N个元素,返回剩下的元素所组成的新Stream。如果原Stream的元素个数大于N,将返回原Stream的后(原Stream长度-N)个元素所组成的新Stream;如果原Stream的元素个数小于或等于N,将返回一个空Stream。

示例:

Stream.of(1, 2, 3, 4, 5).skip(2).forEach(System.out::println);
// 3
// 4
// 5

3.1.8sorted

sorted方法将对原Stream进行排序,返回一个有序列的新Stream。sorted有两种变体sorted(),sorted(Comparator),前者将默认使用Object.equals(Object)进行排序,而后者接受一个自定义排序规则函数(Comparator),可按照意愿排序。

示例:

Stream.of(5, 4, 3, 2, 1)        
	.sorted()        
	.forEach(System.out::println);
// 1
// 2
// 3
// 4
// 5

Stream.of(1, 3, 4, 2, 5)
    .sorted(Comparator.comparingInt(a -> -a))
    .forEach(System.out::println);
// 5
// 4
// 3
// 2
// 1

3.1.9parallel

parallel方法返回并行的等效流。可能会返回自身,因为流已经并行,或者基础(底层)流状态被修改为并行。

并行流就是一个把内容分成多个数据块,并用不同的线程分别处理每个数据块的流。

对顺序流调用parallel方法并不意味着流本身有任何实际的变化。它在内部实际上就是设了一个boolean标志,表示想让调用parallel之后进行的所有操作都并行执行。类似地,只需要对并行流调用sequential方法就可以把它变成顺序流。
请注意,你可能以为把这两个方法结合起来,就可以更细化地控制在遍历流时哪些操作要并行执行,哪些要顺序执行。
但最后一次parallelsequential调用会影响整个流水线。

stream.parallel()
    .filter(...)
    .sequential()
    .map(...)
    .parallel()
    .reduce();

在本例中,流水线会并行执行,因为最后调用的是它。

配置并行流使用的线程池

并行流内部使用了默认的ForkJoinPool,它默认的线程数量就是你的处理器数量,这个值是由Runtime.getRuntime().available
Processors()得到的。
可以通过系统属性java.util.concurrent.ForkJoinPool.common.parallelism来改变线程池大小,如下所示:
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "12");
这是一个全局设置,因此它将影响代码中所有的并行流。反过来说,目前还无法专为某个并行流指定这个值。一般而言,让ForkJoinPool的大小等于处理器数量是个不错的默认值,强烈建议不要修改它。

3.1.9.1高效使用并行流

  • 如果有疑问,测量。并行流并不总是比顺序流快。此外,并行流有时候会和你的直觉不一致,所 以在考虑选择顺序流还是并行流时,第一个也是最重要的建议就是用适当的基准来检查其性能。

  • 留意装箱。自动装箱和拆箱操作会大大降低性能。Java 8中有原始类型流(IntStream、LongStream、DoubleStream)来避免这种操作,但凡有可能都应该用这些流。

  • 有些操作本身在并行流上的性能就比顺序流差。特别是limitfindFirst等依赖于元素顺序的操作,它们在并行流上执行的代价非常大。例如,findAny会比findFirst性能好,因为它不一定要按顺序来执行。总是可以调用unordered方法来把有序流变成无序流。那么,如果需要流中的n个元素而不是专门要前n个的话,对无序并行流调用limit可能会比单个有序流(比如数据源是一个List)更高效。

  • 还要考虑流的操作流水线的总计算成本。设N是要处理的元素的总数,Q是一个元素通过流水线的大致处理成本,则N*Q就是这个对成本的一个粗略的定性估计。Q值较高就意味着使用并行流时性能好的可能性比较大。

  • 对于较小的数据量,选择并行流几乎从来都不是一个好的决定。并行处理少数几个元素的好处还抵不上并行化造成的额外开销。

  • 要考虑流背后的数据结构是否易于分解。例如,ArrayList的拆分效率比LinkedList高得多,因为前者用不着遍历就可以平均拆分,而后者则必须遍历。另外,用range工厂方法创建的原始类型流也可以快速分解。可以自己实现Spliterator来完全掌控分解过程。

  • 流自身的特点,以及流水线中的中间操作修改流的方式,都可能会改变分解过程的性能。
    例如,一个SIZED流可以分成大小相等的两部分,这样每个部分都可以比较高效地并行处理,但筛选操作可能丢弃的元素个数却无法预测,导致流本身的大小未知。

  • 还要考虑终端操作中合并步骤的代价是大是小(例如Collector中的combiner方法)。如果这一步代价很大,那么组合每个子流产生的部分结果所付出的代价就可能会超出通过并行流得到的性能提升。

按照可分解性总结了一些流数据源适不适于并行操作

Stream源 可分解性
ArrayList Excellent极佳
LinkedList Poor差
IntStream.range Excellent极佳
Stream.iterate Poor差
HashSet Good好
TreeSet Good好

3.2终端操作

3.2.1collect

collect方法用于收集

List<Integer> collect = Stream.of(1, 2, 3, 4).collect(Collectors.toList());
System.out.println(collect); // [1, 2, 3, 4]

具体请参考Java8中Collectors语法详解

3.2.2count

count方法将返回Stream中元素的个数。

示例:

long count = Stream.of(1, 2, 3, 4, 5).count();
System.out.println("count:" + count); // count:5

3.2.3forEach

forEach方法用于遍历Stream中的所有元素。

Stream.of(3, 2, 1).forEach(System.out::println);
// 3
// 2
// 1

3.2.4forEachOrdered

forEachOrdered方法与forEach类似,都是遍历Stream中的所有元素,不同的是,如果该Stream预先设定了顺序,会按照预先设定的顺序执行(Stream是无序的),默认为元素插入的顺序。

Stream.of(1,3,2).forEachOrdered(System.out::println);
// 1
// 3
// 2

3.2.5max

max方法根据指定的Comparator,返回一个Optional,该Optional中的value值就是Stream中最大的元素。

Optional<Integer> max = Stream.of(1, 2, 3).max(Integer::compareTo);
System.out.println("max:" + max.get()); // max:3

3.2.6min

min方法根据指定的Comparator,返回一个Optional,该Optional中的value值就是Stream中最小的元素。

Optional<Integer> min = Stream.of(1, 2, 3).min(Integer::compareTo);
System.out.println("min:" + min.get()); // min:1

3.2.7reduce

reduce方法的主要作用是把Stream元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面Stream的第一个、第二个、第n个元素组合。从这个意义上说,字符串拼接、数值的sum、min、max、average都是特殊的reduce。

Integer result = Stream.of(1, 2, 3).reduce(0, (a, b) -> a + b);
System.out.println(result);

Optional<Integer> optionalInteger = Stream.of(1, 2, 3).reduce(Integer::sum);
System.out.println(optionalInteger.get());

reduce有三个变形的方法:

①Optional<T> reduce(BinaryOperator<T> accumulator);

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

Optional<T> reduce(BinaryOperator<T> accumulator);

此方法没有起始值,传入二元操作符BinaryOperator,第一个参数是上次函数执行的返回值(中间结果),第二个参数是Stream中的元素,执行指定的操作,得到的结果会被赋值给下次执行这个函数的第一个参数。这个方法的返回值类型是Optional。

注意: 第一次执行的时候第一个参数的值是Stream中的第一个元素,第二个参数是Stream中的第二个元素。

Optional<Integer> accumulatorResult = Stream.of(1, 2, 3, 4)        
    .reduce((accumulator, item) -> {            
        System.out.println("accumulator: " + accumulator);            
        accumulator += item;            
        System.out.println("item: " + item);            
        System.out.println("accumulator+: " + accumulator);
        System.out.println("--------");            
        return accumulator;        
    });
System.out.println("accumulatorResult: " + accumulatorResult.get());

accumulator: 1
item: 2
accumulator+: 3
--------
accumulator: 3
item: 3
accumulator+: 6
--------
accumulator: 6
item: 4
accumulator+: 10
--------
accumulatorResult: 10

T reduce(T identity, BinaryOperator<T> accumulator);

此方法指定了起始值,如果Stream为空,就直接返回该值;由于方法指定了起始值,直接返回具体的对象,不会返回Optional。

Integer accumulatorResult = Stream.of(1, 2, 3, 4)        
    .reduce(0, (accumulator, item) -> {            
        System.out.println("accumulator: " + accumulator);            
        accumulator += item;            
        System.out.println("item: " + item);            
        System.out.println("accumulator+: " + accumulator);
        System.out.println("--------");            
        return accumulator;        
    });
System.out.println("accumulatorResult: " + accumulatorResult);

accumulator: 0
item: 1
accumulator+: 1
--------
accumulator: 1
item: 2
accumulator+: 3
--------
accumulator: 3
item: 3
accumulator+: 6
--------
accumulator: 6
item: 4
accumulator+: 10
--------
accumulatorResult: 10

1.未定义初始值,从而第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素。
2.定义了初始值,从而第一次执行的时候第一个参数的值是初始值,第二个参数是Stream的第一个元素。

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

由于reduce变形的第一个参数类型是实际返回实例的数据类型,同时其为一个泛型也就是意味着该变形可以返回任意类型的数据。

第一个参数返回实例u,传递要返回的U类型对象的初始化实例u,第二个参数累加器accumulator,可以使用二元Lambda表达式,声明在u上操作处理数据来源t的逻辑,第三个参数组合器combiner,同样是二元操作表达式。

并行操作并且Stream流元素多于1个才会执行第三个参数的combiner组合器函数,返回组合器函数结果,非并行不会执行第三个参数接口,执行完第二个参数接口后就会返回accumulator累加器的结果。

The {@code identity} value must be an identity for the combiner function. This means that for all {@code u}, {@code combiner(identity, u)} is equal to {@code u}. Additionally, the {@code combiner} function must be compatible with the {@code accumulator} function; for all {@code u} and {@code t}, the following must hold:

combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)

{@code identity}值必须是组合器函数的标识。 这意味着对于所有{@code u},{@code combiner(identity, u)}等于{@code u}。 此外,{@code combiner}函数必须与{@code accumulator}函数兼容; 对于所有{@code u}和{@code t},必须满足以下条件:

combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)

此处不易理解,以并行计算sum的和为例

Integer sumReduce = Stream.of(1, 3, 5).parallel().reduce(0,
        (u, t) -> {
            System.out.println("2u:" + u);
            System.out.println("2t:" + t);
            u += t;
            System.out.println("2sum:" + u);
            return u;
        }
        , (u1, u2) -> {
            System.out.println("3u1:" + u1);
            System.out.println("3u2:" + u2);
            u1 += u2;
            System.out.println("3sum:" + u1);
            return u1;
        });
System.out.println("sumReduce:" + sumReduce);
      
2u:0
2t:3
2sum:3
2u:0
2t:5
2sum:5
3u1:3
3u2:5
3sum:8
2u:0
2t:1
2sum:1
3u1:1
3u2:8
3sum:9
sumReduce:9

由于是并行计算,每次返回的结果顺序可能是不同的,但sumReduce的值是相同的。组合器函数combiner使用的是BinaryOperator二元操作符,此接口函数提供两个相同类型参数,并且返回结果与输入参数类型一致的结果,可以看出accumulator累加器计算的是初始值0+Stream单个item的值,u1和u2传入的是累加器accumulator函数计算后的结果,因为要计算sum和,所以combiner组合函数执行的操作是将两个参数累加。

初始值如果设置1进行并行计算,则累加器计算的是初始值1+Stream单个item的值

Integer sumReduce = Stream.of(1, 3, 5).parallel().reduce(1,
        (u, t) -> {
            System.out.println("2u:" + u);
            System.out.println("2t:" + t);
            u += t;
            System.out.println("2sum:" + u);
            return u;
        }
        , (u1, u2) -> {
            System.out.println("3u1:" + u1);
            System.out.println("3u2:" + u2);
            u1 += u2;
            System.out.println("3sum:" + u1);
            return u1;
        });
System.out.println("sumReduce:" + sumReduce);

2u:1
2t:3
2sum:4
2u:1
2t:5
2sum:6
3u1:4
3u2:6
3sum:10
2u:1
2t:1
2sum:2
3u1:2
3u2:10
3sum:12
sumReduce:12

以下如果不进行并行计算,可以看出非并行不会执行第三个参数接口,执行完第二个参数接口后就会返回accumulator累加器的结果,并且此方式相当于reduce变形②T reduce(T identity, BinaryOperator<T> accumulator);

Integer sumReduce = Stream.of(1, 3, 5).reduce(1,
        (u, t) -> {
            System.out.println("2u:" + u);
            System.out.println("2t:" + t);
            u += t;
            System.out.println("2sum:" + u);
            return u;
        }
        , (u1, u2) -> {
            System.out.println("3u1:" + u1);
            System.out.println("3u2:" + u2);
            u1 += u2;
            System.out.println("3sum:" + u1);
            return u1;
        });
System.out.println("sumReduce:" + sumReduce);

2u:1
2t:1
2sum:2
2u:2
2t:3
2sum:5
2u:5
2t:5
2sum:10
sumReduce:10

3.2.8toArray

toArray方法返回一个包含此Stream流的元素的数组。

返回类型 方法
Object[] toArray()
A[] toArray(IntFunction<A[]> generator)

toArray(IntFunction<A[]> generator)返回一个包含此流元素的数组,使用提供的generator函数分配返回的数组,以及分区执行或调整大小可能需要的任何其它数组。
注意:generator函数采用整数,该大小是所需数组的大小,并生成所需大小的数组;可以用数组构造函数引用简洁地表达。

int[] intArray = IntStream.rangeClosed(1, 10).toArray();
System.out.println(Arrays.toString(intArray));

String[] strArray = IntStream.rangeClosed(1, 10).mapToObj(String::valueOf)
    .toArray(String[]::new);
System.out.println(Arrays.toString(strArray));
// String类型数组转换为Integer类型数组
Integer[] integersArray = Arrays.stream(strArray).mapToInt(Integer::valueOf)
    .boxed().toArray(Integer[]::new);
System.out.println(Arrays.toString(integersArray));

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

3.3短路

3.3.1allMatch

allMatch方法用于判断Stream中的元素是否全部满足指定条件。如果全部满足条件返回true,否则返回false。

示例:

boolean allMatch = Stream.of(1, 2, 3, 4)        
	.allMatch(item -> item > 0);
System.out.println("allMatch: " + allMatch); // allMatch: true

3.3.2anyMatch

anyMatch方法用于判断Stream中是否有满足指定条件的元素。如果最少有一个满足条件返回true,否则返回false。

示例:

boolean anyMatch = Stream.of(1, 2, 3, 4)        
	.anyMatch(item -> item > 3);
System.out.println("anyMatch: " + anyMatch); // anyMatch: true

3.3.3noneMatch

noneMatch方法将判断Stream中的所有元素是否满足指定的条件。如果所有元素都不满足条件,返回true;否则,返回false。

boolean noneMatch = Stream.of(1, 2, 3, 4, 5)        
    .noneMatch(item -> item > 10);
System.out.println("noneMatch:" + noneMatch); // noneMatch:true

boolean noneMatchF = Stream.of(1, 2, 3, 4, 5)        
    .noneMatch(item -> item < 3);
System.out.println("noneMatchF:" + noneMatchF); // noneMatchF:false

3.3.4findAny

findAny方法用于获取含有Stream中的某个元素的Optional,如果Stream为空,则返回一个空的Optional。

此操作的行为明确是不确定的,它会自由的选择Stream中的任何元素。这是为了在并行操作中获得最大的性能;代价是同一个源上的多个调用可能不会返回相同的结果。(如果需要稳定的结果,请使用findFirst())

示例:

Optional<Integer> anyParallel = Stream.of(1, 2, 3, 4).parallel().findAny();
System.out.println(anyParallel.orElse(-1));

Optional<Integer> any = Stream.of(1, 2, 3, 4).findAny();
System.out.println(any.orElse(-1));

3.3.5findFirst

findFirst方法用于获取含有Stream中的第一个元素的Optional,如果Stream为空,则返回一个空的Optional。若Stream并未排序,可能返回含有Stream中任意元素的Optional。

Optional<Integer> findFirst = Stream.of(1, 2, 3, 4).findFirst();
System.out.println(findFirst.orElse(-1));

3.3.6limit

limit方法将截取原Stream,截取后Stream的最大长度不能超过指定值N。如果原Stream的元素个数大于N,将截取原Stream的前N个元素;如果原Stream的元素个数小于或等于N,将截取原Stream中的所有元素。

Stream.of(1, 2, 3, 4, 5).limit(2L).forEach(System.out::println);
// 1
// 2

本文参考:
Java 8 中的 Streams API 详解
Java 8系列之Stream的基本语法详解
简洁又快速地处理集合——Java8 Stream(上)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章