前面我们说到collect方法用于收集元素,并转换成我们需要的格式。而collect方法的传参我们常用的不是自己实现Collector接口,而是使用Collectors类的方法,接下来就以例子说明一下Collectors类的常用方法

1、toList

Collectors.toList用于将流元素收集成一个List集合,无需传参

1
2
3
4
5
6
7
8
// 例子1,获取大于3的数字并放入List集合中
List<Integer> collect = Stream.of(1, 2, 3, 4, 5, 6)
.filter(i->{
return i > 3;
})
.collect(Collectors.toList());
System.out.println(collect);
// 结果 [4, 5, 6]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Data
@AllArgsConstructor
class User {
private Integer id;
private String userName;
private Integer age;

}

public class Test {
public static void main(String[] args) {

User user1 = new User(1, "张三", 18);
User user2 = new User(2, "李四", 20);
User user3 = new User(3, "王五", 22);

// 例子2,获取年龄大于19岁的用户并放入List集合中
List<User> collect = Stream.of(user1, user2, user3)
.filter(u->{
return u.getAge() > 19;
})
.collect(Collectors.toList());
System.out.println(collect);
// 结果 [User(id=2, userName=李四, age=20), User(id=3, userName=王五, age=22)]
}
}

2、toSet

Collectors.toSet用于将流元素收集成一个Set集合,无需传参

1
2
3
4
5
6
7
8
// 例子1,获取大于3的数字并放入Set集合中
Set<Integer> collect = Stream.of(1, 2, 3, 4, 5, 6)
.filter(i->{
return i > 3;
})
.collect(Collectors.toSet());
System.out.println(collect);
// 结果 [4, 5, 6]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Data
@AllArgsConstructor
class User {
private Integer id;
private String userName;
private Integer age;

}

public class Test {
public static void main(String[] args) {

User user1 = new User(1, "张三", 18);
User user2 = new User(2, "李四", 20);
User user3 = new User(3, "王五", 22);
User user4 = new User(3, "王五", 22);

// 例子2,获取年龄大于19岁的用户并放入Set集合中
Set<User> collect = Stream.of(user1, user2, user3, user4)
.filter(u->{
return u.getAge() > 19;
})
.collect(Collectors.toSet());
System.out.println(collect);
// 结果 [User(id=3, userName=王五, age=22), User(id=2, userName=李四, age=20)]
}
}

3、toMap

Collectors.toMap可以将流元素返回成map对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Data
@AllArgsConstructor
class User {
private Integer id;
private String userName;
private Integer age;

}

public class Test {
public static void main(String[] args) {

User user1 = new User(1, "张三", 18);
User user2 = new User(2, "李四", 20);
User user3 = new User(3, "王五", 22);

// 例子,获取一个key:userId,value:userName 的map
Map<Integer, String> collect = Stream.of(user1, user2, user3)
.collect(Collectors.toMap(u -> u.getId(), u -> u.getUserName(), (oldValue, newValue) -> newValue));
System.out.println(collect);
// 结果 {1=张三, 2=李四, 3=王五}
}
}

Collectors.toMap还有两个参数和四个参数的使用方法,两个参数只需要传key和value的生成函数,但是在有重复的key时会报异常。四个参数的,第四个参数是生成的map类型,三个参数是默认的生成的是HashMap类型的,上述也能改成

1
2
3
Map<Integer, String> collect = Stream.of(user1, user2, user3)
.collect(Collectors.toMap(u -> u.getId(), u -> u.getUserName(), (oldValue, newValue) -> newValue, LinkedHashMap::new));
System.out.println(collect);

使生成的map类型是LinkedHashMap

4、joining

Collectors.joining用于将流元素拼接成字符串返回,但是要求流元素要是字符格式,有三种传参方式,如下方拼接1, 2, 3, 4, 5, 6的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 第一种传参方式:无传参
String collect = Stream.of(1, 2, 3, 4, 5, 6)
.map(i -> {
return i.toString();
})
.collect(Collectors.joining());
System.out.println(collect);
// 结果 123456

// 第二种传参方式:传拼接符
String collect = Stream.of(1, 2, 3, 4, 5, 6)
.map(i -> {
return i.toString();
})
.collect(Collectors.joining("-"));
System.out.println(collect);
// 结果 1-2-3-4-5-6

// 第三种传参方式:传拼接符和前后缀
String collect = Stream.of(1, 2, 3, 4, 5, 6)
.map(i -> {
return i.toString();
})
.collect(Collectors.joining("-" ,"前", "后"));
System.out.println(collect);
// 结果 前1-2-3-4-5-6后

5、mapping

Collectors.mapping将流元素进行映射,需要传两个参数,一个是映射函数,一个是收集方法,效果和mapcollect一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Data
@AllArgsConstructor
class User {
private Integer id;
private String userName;
private Integer age;

}

public class Test {
public static void main(String[] args) {

User user1 = new User(1, "张三", 18);
User user2 = new User(2, "李四", 20);
User user3 = new User(3, "王五", 22);

// 例子,获取用户的名称列表
List<String> collect = Stream.of(user1, user2, user3)
.collect(Collectors.mapping(
u -> u.getUserName(),
Collectors.toList()
));
System.out.println(collect);
// 结果 [张三, 李四, 王五]
}
}

基本等价于

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Data
@AllArgsConstructor
class User {
private Integer id;
private String userName;
private Integer age;

}

public class Test {
public static void main(String[] args) {

User user1 = new User(1, "张三", 18);
User user2 = new User(2, "李四", 20);
User user3 = new User(3, "王五", 22);

// 例子,获取用户的名称列表
List<String> collect = Stream.of(user1, user2, user3)
.map(u -> u.getUserName())
.collect(Collectors.toList());
System.out.println(collect);
// 结果 [张三, 李四, 王五]
}
}

在后面的groupingBy还会有其他例子

6、groupingBy

Collectors.groupingBy用于给流元素进行分组,如下例子

先定义一个User类和userList的集合

1
2
3
4
5
6
7
8
9
@Data
@AllArgsConstructor
public class User {
private Integer id;
private String userName;
private Integer age;
private String province;
private Integer workDuration;
}
1
2
3
4
5
6
7
8
9
10
11
List<User> userList = new ArrayList<>();
userList.add(new User(1, "张三", 18, "广东省", 1800));
userList.add(new User(2, "李四", 19, "湖南省", 2500));
userList.add(new User(3, "王五", 20, "湖北省", 1526));
userList.add(new User(4, "赵六", 18, "湖北省", 2775));
userList.add(new User(5, "牛七", 25, "吉林省", 5778));
userList.add(new User(6, "张大炮", 24, "广东省", 2146));
userList.add(new User(7, "李小明", 21, "湖北省", 3789));
userList.add(new User(8, "赵有钱", 26, "四川省", 1458));
userList.add(new User(9, "王书", 26, "广东省", 1496));
userList.add(new User(10, "吴子明 ", 22, "吉林省", 4523));
6.1 例子1:根据省份进行分组
1
2
3
Map<String, List<User>> collect = userList.stream()
.collect(Collectors.groupingBy(User::getProvince));
System.out.println(collect);

结果为:

image-20240211214142286

6.2 例子2、获取各个省份的用户数
1
2
3
4
5
Map<String, Long> collect = userList.stream()
.collect(Collectors.groupingBy(User::getProvince, Collectors.counting()));
System.out.println(collect);
// 结果:
// {湖北省=3, 湖南省=1, 广东省=3, 吉林省=2, 四川省=1}
6.3 例子3、根据省份进行分组,但是value只需要userName
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 1、根据省份分组后,userName用List接收
Map<String, List<String>> collect = userList.stream()
.collect(Collectors.groupingBy(
User::getProvince,
Collectors.mapping(User::getUserName, Collectors.toList())
));
System.out.println(collect);
// 结果:
// {湖北省=[王五, 赵六, 李小明], 湖南省=[李四], 广东省=[张三, 张大炮, 王书], 吉林省=[牛七, 吴子明 ], 四川省=[赵有钱]}

// 2、根据省份分组后,userName拼接成字符串
Map<String, String> collect = userList.stream()
.collect(Collectors.groupingBy(
User::getProvince,
Collectors.mapping(User::getUserName, Collectors.joining(","))
));
System.out.println(collect);
// 结果:
//{湖北省=王五,赵六,李小明, 湖南省=李四, 广东省=张三,张大炮,王书, 吉林省=牛七,吴子明 , 四川省=赵有钱}
6.4 例子4、获取各个省份的工作时长之和
1
2
3
4
5
6
7
8
Map<String, Integer> collect = userList.stream()
.collect(Collectors.groupingBy(
User::getProvince,
Collectors.summingInt(user -> user.getWorkDuration()))
);
System.out.println(collect);
// 结果:
// {湖北省=8090, 湖南省=2500, 广东省=5442, 吉林省=10301, 四川省=1458}

7、collectingAndThen

Collectors.collectingAndThen先对流进行一个收集,然后对收集的数据进行二次处理。该方法需要两个参数,第一个参数是收集的Collector对象,第二个参数的二次处理的函数,并且第一个参数的返回对象是第二个参数的入参对象

7.1 例子1、将一串数字进行去重后转成List
1
2
3
4
5
6
7
8
9
10
11
12
13
14
List<Integer> collect = Stream.of(1, 2, 3, 4, 4, 5, 6, 2, 3, 5)
.collect(Collectors.collectingAndThen(
Collectors.toSet(),
ArrayList::new
));
System.out.println(collect);
// 结果 [1, 2, 3, 4, 5, 6]

// 上面例子等价于
List<Integer> collect = Stream.of(1, 2, 3, 4, 4, 5, 6, 2, 3, 5)
.collect(Collectors.toSet())
.stream()
.collect(Collectors.toList());
System.out.println(collect);
7.2 例子2、获取age最大的用户名

下面例子中的userList第6点groupingBy的userList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
String collect = userList.stream()
.collect(Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparing(User::getAge)),
v -> v.get().getUserName()
));
System.out.println(collect);
// 结果 赵有钱

// 上面例子等价于
String collect = userList.stream()
.collect(Collectors.maxBy(Comparator.comparing(User::getAge)))
.get().getUserName();
System.out.println(collect);
// 结果 赵有钱
7.3 例子3、根据用户的省份去重用户

下面例子中的userList第6点groupingBy的userList

1
2
3
4
5
6
7
8
9
10
11
12
13
List<User> collect = userList.stream()
.collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getProvince))),
ArrayList::new
));
System.out.println(collect);

// 上面例子等价于
List<User> collect = userList.stream()
.collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getProvince))))
.stream()
.collect(Collectors.toList());
System.out.println(collect);

结果如下:

image-20240212104223415

由上面的例子可以看出,collectingAndThen是元素收集加额外处理两个步骤的合并