Java Stream 流的编程思想
常用方法
流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:
-
- 延迟方法:返回值类型仍然是 Stream 接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为延迟方法。)
- 终结方法:返回值类型不再是 Stream 接口自身类型的方法,因此不再支持类似 StringBuilder 那样的链式调用。本小节中,终结方法包括 count 和 forEach 方法。
1、逐一处理:forEach
虽然方法名字叫 forEach ,但是与for循环中的“for-each”昵称不同。
| 1 | void
forEach(Consumer ``super
T> action);
|
| - | - |
该方法接收一个 Consumer 接口函数,会将每一个流元素交给该函数进行处理。
Consumer 接口
| 12 | java.util.function.Consumer接口是一个消费型接口。``Consumer接口中包含抽象方法``void
accept(T t),意为消费一个指定泛型的数据。
|
| - | - |
基本使用:
1 import java.util.stream.Stream;
2 public class DemoStreamForEach {
3 public static void main(String[] args) {
4 Stream<String> stream = Stream.of("张无忌", "张三丰", "周芷若");
5 stream.forEach(name‐> System.out.println(name));
6 }
7 }
2、过滤:filter
可以通过 filter 方法将一个流转换成另一个子集流。方法签名:
| 1 | Stream filter(Predicate ``super
T> predicate);
|
| - | - |
该接口接收一个 Predicate 函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件。
Predicate 接口:
| 1 | boolean
test(T t);
|
| - | - |
该方法将会产生一个boolean值结果,代表指定的条件是否满足。如果结果为true,那么Stream流的 filter 方法将会留用元素;如果结果为false,那么 filter 方法将会舍弃元素。
基本使用:
1 import java.util.stream.Stream;
2 public class DemoStreamFilter {
3 public static void main(String[] args) {
4 Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
5 Stream<String> result = original.filter(s ‐> s.startsWith("张"));
6 }
7 }
在这里通过Lambda表达式来指定了筛选的条件:必须姓张
3、映射:map
如果需要将流中的元素映射到另一个流中,可以使用 map 方法。方法签名:
| 1 | Stream map(Function ``super
T, ? ``extends
R> mapper);
|
| - | - |
该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。
** Function 接口:**
| 1 | R apply(T t);
|
| - | - |
这可以将一种T类型转换成为R类型,而这种转换的动作,就称为“映射”。
基本使用:
1 import java.util.stream.Stream;
2 public class DemoStreamMap {
3 public static void main(String[] args) {
4 Stream<String> original = Stream.of("10", "12", "18");
5 Stream<Integer> result = original.map(str‐>Integer.parseInt(str));
6 }
7 }
这段代码中, map 方法的参数通过方法引用,将字符串类型转换成为了int类型(并自动装箱为 Integer 类对象)。
4、统计个数:count
正如旧集合 Collection 当中的 size 方法一样,流提供 count 方法来数一数其中的元素个数:
| 1 | long
count();
|
| - | - |
该方法返回一个long值代表元素个数(不再像旧集合那样是int值)。
基本使用:
1 import java.util.stream.Stream;
2 public class DemoStreamCount {
3 public static void main(String[] args) {
4 Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
5 Stream<String> result = original.filter(s ‐> s.startsWith("张"));
6 System.out.println(result.count()); // 2
7 }
8 }
5、取用前几个:limit
limit 方法可以对流进行截取,只取用前n个。方法签名
| 1 | Stream limit(``long
maxSize);
|
| - | - |
参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。
基本使用:
1 import java.util.stream.Stream;
2 public class DemoStreamLimit {
3 public static void main(String[] args) {
4 Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
5 Stream<String> result = original.limit(2);
6 System.out.println(result.count()); // 2
7 }
8 }
6、跳过前几个:skip
如果希望跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流:
| 1 | Stream skip(``long
n);
|
| - | - |
如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。
基本使用:
1 import java.util.stream.Stream;
2 public class DemoStreamSkip {
3 public static void main(String[] args) {
4 Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
5 Stream<String> result = original.skip(2);
6 System.out.println(result.count()); // 1
7 }
8 }
7、组合:concat
如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat :
| 1 | static
Stream concat(Stream ``extends
T> a, Stream ``extends
T> b)
|
| - | - |
注意:这是一个静态方法,与 java.lang.String 当中的 concat 方法是不同的。
基本使用:
1 import java.util.stream.Stream;
2 public class DemoStreamConcat {
3 public static void main(String[] args) {
4 Stream<String> streamA = Stream.of("张无忌");
5 Stream<String> streamB = Stream.of("张翠山");
6 Stream<String> result = Stream.concat(streamA, streamB);
7 }
8 }
五、案例
题目:现在有两个 ArrayList 集合存储队伍当中的多个成员姓名, 依次进行以下若干操作步骤:
| 1234567 | 1``. 第一个队伍只要名字为``3``个字的成员姓名;存储到一个新集合中。``2``. 第一个队伍筛选之后只要前``3``个人;存储到一个新集合中。``3``. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。``4``. 第二个队伍筛选之后不要前``2``个人;存储到一个新集合中。``5``. 将两个队伍合并为一个队伍;存储到一个新集合中。``6``. 根据姓名创建 Person 对象;存储到一个新集合中。``7``. 打印整个队伍的Person对象信息。
|
| - | - |
两个队伍(集合)的代码如下:
1 import java.util.ArrayList;
2 import java.util.List;
3 public class DemoArrayListNames {
4 public static void main(String[] args) {
5 //第一支队伍
6 ArrayList<String> one = new ArrayList<>();
7 one.add("迪丽热巴");
8 one.add("宋远桥");
9 one.add("苏星河");
10 one.add("石破天");
11 one.add("石中玉");
12 one.add("老子");
13 one.add("庄子");
14 one.add("洪七公");
15 //第二支队伍
16 ArrayList<String> two = new ArrayList<>();
17 two.add("古力娜扎");
18 two.add("张无忌");
19 two.add("赵丽颖");
20 two.add("张三丰");
21 two.add("尼古拉斯赵四");
22 two.add("张天爱");
23 two.add("张二狗");
24 // ....
25 }
26 }
Person 类的代码为:
1 public class Person {
2 private String name;
3
4 public Person() {
5 }
6
7 public Person(String name) {
8 this.name = name;
9 }
10
11 @Override
12 public String toString() {
13 return "Person{name='" + name + "'}";
14 }
15
16 public String getName() {
17 return name;
18 }
19
20 public void setName(String name) {
21 this.name = name;
22 }
23 }
**方式一:使用传统的for循环(或增强for循环) **
代码实现:
1 public class DemoArrayListNames {
2 public static void main(String[] args) {
3 List<String> one = new ArrayList<>();
4 // ...
5 List<String> two = new ArrayList<>();
6 // ...
7
8 // 第一个队伍只要名字为3个字的成员姓名;
9 List<String> oneA = new ArrayList<>();
10 for (String name : one) {
11 if (name.length() == 3) {
12 oneA.add(name);
13 }
14 }
15
16 // 第一个队伍筛选之后只要前3个人;
17 List<String> oneB = new ArrayList<>();
18 for (int i = 0; i < 3; i++) {
19 oneB.add(oneA.get(i));
20 }
21
22 // 第二个队伍只要姓张的成员姓名;
23 List<String> twoA = new ArrayList<>();
24 for (String name : two) {
25 if (name.startsWith("张")) {
26 twoA.add(name);
27 }
28 }
29
30 // 第二个队伍筛选之后不要前2个人;
31 List<String> twoB = new ArrayList<>();
32 for (int i = 2; i < twoA.size(); i++) {
33 twoB.add(twoA.get(i));
34 }
35
36 // 将两个队伍合并为一个队伍;
37 List<String> totalNames = new ArrayList<>();
38 totalNames.addAll(oneB);
39 totalNames.addAll(twoB);
40 // 根据姓名创建Person对象;
41 List<Person> totalPersonList = new ArrayList<>();
42 for (String name : totalNames) {
43 totalPersonList.add(new Person(name));
44 }
45 // 打印整个队伍的Person对象信息。
46 for (Person person : totalPersonList) {
47 System.out.println(person);
48 }
49 }
50 }
方式二:使用 Stream 流式处理方式。
代码实现:
1 import java.util.ArrayList;
2 import java.util.List;
3 import java.util.stream.Stream;
4 public class DemoStreamNames {
5 public static void main(String[] args) {
6 List<String> one = new ArrayList<>();
7 // ...
8 List<String> two = new ArrayList<>();
9 // ...
10 // 第一个队伍只要名字为3个字的成员姓名;
11 // 第一个队伍筛选之后只要前3个人;
12 Stream<String> streamOne = one.stream().filter(s->s.length() == 3).limit(3);
13
14 // 第二个队伍只要姓张的成员姓名;
15 // 第二个队伍筛选之后不要前2个人;
16 Stream<String> streamTwo = two.stream().filter(s->s.startsWith("张")).skip(2);
17
18 // 将两个队伍合并为一个队伍;
19 // 根据姓名创建Person对象;
20 // 打印整个队伍的Person对象信息。
21 Stream.concat(streamOne, streamTwo).map(Person::new).forEach(System.out::println);
22 }
23 }