Java 8 Stream API

Java 8 中的 Stream 被定义为源的一系列元素,它们可以聚合操作。这里的源可以是CollectionsArrays,为源提供元素。Stream 会保持元素在源中的顺序,聚合操作或批量操作是在为每个元素进行相同的操作。java 8 中的 Stream 被设计为链式调用,它的大多数方法返回的是Stream本身。这种方式也被称为管道


1. Java Stream VS Collection

我们平时在网站在线看视频,当开始观看时,视频文件的一部分会加载到我们的电脑,然后进行播放。你不需要在观看前下载完整的视频文件,这就是流。

Collection是内存中的数据结构,Collection中的每个元素在加载到Collection之前都完成计算的操作。而流的结构是固定的,它的元素是按需计算。这具有很大的优势,数据仅在用户需要时,才会提供相应的数据,这类似生产者 - 消费者的关系。

在Java中,java.util.Stream表示流,它可以进行一次或多少操作。流的操作分两种:一种是中间操作返回自身;另一种是返回一个明确类型值的终端操作,所有我们可以链式调用多个方法。流是在源上创建的,如java.util.Collection中的listsetmap不支持流操作)。流操作可以顺序执行,也可以并行执行。

流的特征:

不是一个数据结构

支持lambdas

不支持索引访问

可以很容易的作为列表和数组输出

支持延迟操作

可并行操作


2.创建流的不同方式

下面是常用的几种:


2.1 Stream.of(val1, val2, val3......)

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

stream.forEach(p -> System.out.println(p));


2.2 Stream.of(arrayOfElements)

Stream<Integer> stream = Stream.of(new Integer[]{1, 2, 3, 4, 5, 6});

stream.forEach(p -> System.out.println(p));


2.3 List.stream()

List<Integer> list = new ArrayList<>();

for (int i = 0; i < 10; i++) {

    list.add(i);

}

Stream<Integer> stream = list.stream();

stream.forEach(p -> System.out.println(p));


2.4 Stream.generate() or Stream.iterate()

Stream<Date> streamGenerate = Stream.generate(() -> {return new Date();});

streamGenerate.forEach(p -> System.out.println(p));

Stream<Integer> streamIterate = Stream.iterate(1, n -> n + 1);

streamIterate.forEach(p -> System.out.println(p));


2.5 String chars or String tokens

IntStream stream = "123456_abcdef".chars();

stream.forEach(p -> System.out.println(p));

// or

Stream<String> stream = Stream.of("A$B$C".split("\\$"));

stream.forEach(p -> System.out.println(p));


Stream.Buider和一些中间方法返回流的,就不全列了。


3. 将流转为集合

流转集合其实是将流的所有数据全部放到集合中。


3.1 Stream 转为 List - Stream.collect(Collectors.toList())

List<Integer> list = new ArrayList<>();

for (int i = 0; i < 10; i++) {

    list.add(i);

}

Stream<Integer> stream = list.stream();

List<Integer> evenNumbers = stream.filter(i -> i % 2 == 0).collect(Collectors.toList());

System.out.println(evenNumbers);


3.2 Stream 转为 Array - Stream.toArray(EntryType::new)

List<Integer> list = new ArrayList<>();

for (int i = 0; i < 10; i++) {

    list.add(i);

}

Stream<Integer> stream = list.stream();

Integer[] evenNumbers = stream.filter(i -> i % 2 == 0).toArray(Integer[]::new);

Stream.of(evenNumbers).forEach(p -> System.out.println(p));


还有很多其它的方式将stream转为setmap,这些方式都在Collectors类中。



4.流的核心操作

流的接口中有一系列方法,这里列出一些重要的操作方法。

首先构建一个列表,下面例子都基于这个列表:

List<String> memberNames = new ArrayList<>();

memberNames.add("Amitabh");

memberNames.add("Shekhar");

memberNames.add("Aman");

memberNames.add("Rahul");

memberNames.add("Shahrukh");

memberNames.add("Salman");

memberNames.add("Yana");

memberNames.add("Lokesh");


4.1 中间方法

中间方法返回它自身,可以链式调用。


4.1.1 Stream.filter()

filter方法通过给定的判定去过滤Stream中的元素

memberNames.stream().filter(p -> p.startsWith("A")).forEach(System.out::println);


输出:

Amitabh

Aman


4.1.2 Stream.map()

Stream中的每个元素应用map中给定的方法

memberNames.stream().filter(p -> p.startsWith("A")).map(String::toUpperCase).forEach(System.out::println);


输出:

AMITABH

AMAN


4.1.3 Stream.sorted()

返回Stream排序的视图,默认自然序,除非自定义排序。

memberNames.stream().sorted().map(String::toUpperCase).forEach(System.out::println);


注意:sorted方法仅仅创建Stream的排序视图,不会更改集合的原始顺序。


4.2 终端方法

终端方法返回具体的类型结果,而不是流本身


4.2.1 Stream.collect()

方法从Stream中接收元素保存到参数中指定的集合

List<String> list = memberNames.stream().sorted().map(String::toUpperCase).collect(Collectors.toList());

System.out.println(list);


结果:

[AMAN, AMITABH, LOKESH, RAHUL, SALMAN, SHAHRUKH, SHEKHAR, YANA]

         

4.2.2 Stream.match()

match有多种判定方式,结果返回一个boolean

boolean m1 = memberNames.stream().anyMatch(p -> p.startsWith("A")); // 包含

boolean m2 = memberNames.stream().allMatch(p -> p.startsWith("A")); // 全部

boolean m3 = memberNames.stream().noneMatch(p -> p.startsWith("A")); // 不包含



System.out.println(m1);

System.out.println(m2);

System.out.println(m3);


结果:

true

false

false

         

4.2.3 Stream.count()

方法返回一个long数值,计数Stream中元素的数量

long count = memberNames.stream().filter(p -> p.startsWith("A")).count();

System.out.println(count); // 2


4.2.4 Stream.reduce()

方法会减少流的元素,返回最后值

memberNames.stream().reduce((s1, s2) -> s1 + "#" + s2).ifPresent(p -> System.out.println(p));


计算数字1-10的和

Integer[] numbers = new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

Stream.of(numbers).reduce((n1, n2) -> n1 + n2).ifPresent(p -> System.out.println(p));


5. Stream 短路操作

通常流会为集合中所有元素进行操作,但有时我们希望匹配某元素后停止后续操作。通常在外部循环,使用if-else即可。而在流的内部循环中,有明确的方法可以做到这点。


5.1 Stream.anyMatch()

一旦满足给定的判断条件,就立即返回,不再处理后续元素

boolean match = memberNames.stream().anyMatch(p -> p.startsWith("A"));

System.out.println(match); // true


5.2 Stream.findFirst()

它将从流中返回第一个元素,不处理后续元素。

String name = memberNames.stream().filter(p -> p.startsWith("S")).findFirst().get();

System.out.println(name);  # Shekhar


6. Java Stream 的并行

多线程可以提升执行的性能,但编写健壮的多线程程序不易。Stream提供了健壮、容易编写的并行方法,只需要将顺序流stream替换成并行流parallelStream。上面所有例子中,你用parallelStream()替换stream(),就开启了并行。

List<Integer> list = new ArrayList<>();

for (int i = 0; i < 20; i++) {

    list.add(i);

}

list.parallelStream().filter(p -> p % 2 == 0).forEach(p -> System.out.println(p));


结果

6

2

12

18

4

8

0

10

16

14


并行性可能带来不确定性和不期望的资源竞争。


 

展开阅读全文