Lambda表达式

前言

在日常开发中,我们很多时候需要用到Java 8Lambda表达式,它允许把函数作为一个方法的参数,让我们的代码更优雅,更简洁。以下为常用的表达式写法。

.collect()用来起保存作用的函数

List转Map

Collectors.toMap可以把一个List数组转成一个Map,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TestLanbda {

public static void main(String[] args) {
List<UserInfo> userInfoList = new ArrayList<>();
userInfoList.add(new UserInfo(1L, "采蘑菇的小姑娘",18));
userInfoList.add(new UserInfo(2L, "程序员男孩", 22));
userInfoList.add(new UserInfo(2L, "捡瓶子的小姑娘", 20));

/**
* List转Map
* 使用Collectors.toMap的时候,如果有重复会报错,所以需要加(k1,k2) -> k1
* (k1, k2) -> k1 表示,如果有重复的key,则保留第一个,舍弃第二个
*/
Map<Long,UserInfo> userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserInfo::getUserId, userInfo -> userInfo, (k1, k2) -> k1));
userInfoMap.values().forEach(a -> System.out.println(a.getUserName()));
}
}

使用toMap()函数之后,返回的就是一个Map了,自然会需要keyvalue

第一个参数用来生成key
第二个参数用来生成value
第三个参数用在key值冲突的情况下:如果新元素产生的key在Map中已经出现过了,第三个参数就会定义解决的方法。

在.collect(Collectors.toMap(Person::getId, v -> v, (a,b) -> a)):
第一个参数:Person:getId表示选择Person的getId作为map的key值;
第二个参数:v -> v表示选择将原来的对象作为Map的value值
第三个参数:(a,b) -> a中,如果a与b的key值相同,选择a作为那个key所对应的value值。

类似的,还有Collectors.toList()、Collectors.toSet(),表示把对应的流转化为List或Set。


Filter()过滤

从数组集合中,过滤掉不符合条件的元素,留下符合条件的元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<UserInfo> userInfoList = new ArrayList<>();
userInfoList.add(new UserInfo(1L, "采蘑菇的小姑娘",18));
userInfoList.add(new UserInfo(2L, "程序员男孩", 22));
userInfoList.add(new UserInfo(2L, "捡瓶子的小姑娘", 20));

/**
* filter过滤,留下年龄大于18的用户
*/
List<UserInfo> userInfoResultList = userInfoList.stream().filter(user -> user.getAge() > 18).collect(Collectors.toList());
userInfoResultList.forEach(a -> System.out.println(a.getUserName()));

// 运行结果
程序员男孩
捡瓶子的小姑娘


forEach()循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* forEach 遍历集合List集合
*/
List<String> userNameList = Arrays.asList("采蘑菇的小姑娘","程序员男孩","捡瓶子的小姑娘");
userNameList.forEach(System.out::println);

HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("姓名", "采蘑菇的小姑娘");
hashMap.put("职业", "程序员男孩");
hashMap.put("昵称", "捡瓶子的小姑娘");
/**
* forEach 遍历集合Map
*/
hashMap.forEach((k, v) -> System.out.println(k + ":\t" + v));

// 运行结果
采蘑菇的小姑娘
程序员男孩
捡瓶子的小姑娘
姓名:采蘑菇的小姑娘
职业:程序员男孩
昵称:捡瓶子的小姑娘


groupingBy分组

提到分组,相信大家都会想到SQLgroup by。我们经常需要一个List做分组操作。比如,按城市分组用户。在Java8之前,是这么实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<UserInfo> userInfoList = new ArrayList<>();
userInfoList.add(new UserInfo(1L, "采蘑菇的小姑娘",18, "沈阳"));
userInfoList.add(new UserInfo(2L, "程序员男孩", 22, "深圳"));
userInfoList.add(new UserInfo(2L, "捡瓶子的小姑娘", 20, "沈阳"));

Map<String, List<UserInfo>> result = new HashMap<>();
for (UserInfo userInfo : UserInfoList) {
String city = userInfo.getCity();
List<UserInfo> userInfos = result.get(city);
if (userInfos == null) {
userInfos = new ArrayList<>();
result.put(city, userInfos);
}
userInfos.add(userInfo);
}

而使用Java8的groupingby分组器,则很简单:

1
2
Map<String,List<UserInfo>> result = userInfoList.stream()
.collect(Collectors.groupingBy(UserInfo::getCity))


sorted + Comparator排序

在实际编写代码中,排序的需求比较多,使用sorted+Comparator排序可以简化很多

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
28
List<UserInfo> userInfoList = new ArrayList<>();
userInfoList.add(new UserInfo(1L, "采蘑菇的小姑娘",18));
userInfoList.add(new UserInfo(2L, "程序员男孩", 22));
userInfoList.add(new UserInfo(2L, "捡瓶子的小姑娘", 20));

/**
* sorted + Comparator.comparing 排序列表
*/
userInfoList = userInfoList.stream().sorted(Comparator.comparing(UserInfo::getAge)).collect(Collectors.toList());
System.out.println("开始升序排序")
userInfo.forEach(a -> System.out.println(a.toString()));

/**
* 如果想要降序排序,可以使用加reversed()
*/
System.out.println("开始降序排序")
userInfoList = userInfoList.stream().sorted(Comparator.comparing(UserInfo::getAge).reversed()).collect(Collectors.toList());
userInfo.forEach(a -> System.out.println(a.toString()));

// 运行结果
开始升序排序
userInfo{userId = 1, userName = '采蘑菇的小姑娘', age = 18}
userInfo{userId = 3, userName = '捡瓶子的小姑娘', age = 20}
userInfo{userId = 2, userName = '程序员男孩', age = 22}
开始降序排序
userInfo{userId = 2, userName = '程序员男孩', age = 22}
userInfo{userId = 3, userName = '捡瓶子的小姑娘', age = 20}
userInfo{userId = 1, userName = '采蘑菇的小姑娘', age = 18}


distinct()去重

1
2
3
4
5
6
7
8
9
List<String> list = Arrays.asList("A","B","F","A","C");
List<String> temp = list.stream().distinct().collect(Collectors.toList());
temp.forEach(System.out::println);

//运行结果
A
B
F
C


findFirst()返回第一个

1
2
3
4
5
List<String> list = Arrays.asList("A","B","F","A","C");
list.stream().findFirst().ifPresent(System.out::println);

// 运行结果
A


anyMatch()和allMatch()匹配

anyMatch()检查流是否包含至少一个满足要求

1
2
3
4
5
Stream<String> stream = Stream.of("A","B","C","D");
boolean match = stream.anyMatch(s -> s.contains("C"));
System.out.println(match);
//输出
true

allMatch()检查流是否所有都满足
1
2
3
4
5
Stream<String> stream = Stream.of("A","B","C","D");
boolean match = stream.anyMatch(s -> s.length() == 1);
System.out.println(match);
//输出
true


常用函数式接口

函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口
函数式接口可以被隐式转换为Lambda表达式
Lambda表达式和方法引用(实际上也可认为是Lambda表达式)上

常见的函数式接口

  • Function<T, R>(转换型):接收一个输入参数,返回一个结果
  • Comsumer<T>(消费型):接收一个输入参数,并且无返回操作
  • Predicate<T>(判断型):接收一个输入参数,并且返回布尔值结果
  • Supplier<T>(供给型):无参数,无返回结果