lambda常用语法

使用javaProgrammers和phpProgrammers两个泛型为Person的List集合来做示例

关于lambda表达式的认识,可看java中的lambda表达式浅析,本篇讲的是stream的日常使用,当然也会结合lambda来编写一些,由于是小白,所以本篇只能不断更新,用到哪更到哪吧,当笔记用用还蛮不错。

目录

  • [TOC]

使用Lambdas和Streams

Stream是对集合的包装,通常和lambda一起使用。 使用lambdas可以支持许多操作,如 map, filter, limit, sorted, count, min, max, sum, collect 等等。 同样,Stream使用懒运算,他们并不会真正地读取所有数据,遇到像getFirst() 这样的方法就会结束链式语法。:heart_eyes:

forEach方法

1
2
3
System.out.println("所有程序员的姓名:");  
javaProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
phpProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

同样使用forEach方法,增加程序员的工资5%:

1
2
3
4
5
System.out.println("给程序员加薪 5% :");  
Consumer<Person> giveRaise = e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary());

javaProgrammers.forEach(giveRaise);
phpProgrammers.forEach(giveRaise);

filter方法过滤需要的条件

1
2
3
4
System.out.println("下面是月薪超过 $1,400 的PHP程序员:")  
phpProgrammers.stream()
.filter((p) -> (p.getSalary() > 1400))
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

也可以定义过滤器,然后重用它们来执行其他操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 定义 filters  
Predicate<Person> ageFilter = (p) -> (p.getAge() > 25);
Predicate<Person> salaryFilter = (p) -> (p.getSalary() > 1400);
Predicate<Person> genderFilter = (p) -> ("female".equals(p.getGender()));

System.out.println("下面是年龄大于 24岁且月薪在$1,400以上的女PHP程序员:");
phpProgrammers.stream()
.filter(ageFilter)
.filter(salaryFilter)
.filter(genderFilter)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

// 重用filters
System.out.println("年龄大于 24岁的女性 Java programmers:");
javaProgrammers.stream()
.filter(ageFilter)
.filter(genderFilter)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

limit方法

可以限制结果集的个数

1
2
3
4
5
6
7
8
9
10
11
System.out.println("最前面的3个 Java programmers:");  
javaProgrammers.stream()
.limit(3)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));


System.out.println("最前面的3个女性 Java programmers:");
javaProgrammers.stream()
.filter(genderFilter)
.limit(3)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

skip方法

通常可以结合limit方法来说明跳过第几个来进行查询,需要注意的是skip方法需要放在limit方法前面执行

sorted方法排序

在下面的例子中,将根据名字和薪水排序Java程序员,放到一个list中,然后显示列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
System.out.println("根据 name 排序,并显示前5个 Java programmers:");  
List<Person> sortedJavaProgrammers = javaProgrammers
.stream()
.sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName())))
.limit(5)
.collect(toList());

sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));

System.out.println("根据 salary 排序 Java programmers:");
sortedJavaProgrammers = javaProgrammers
.stream()
.sorted( (p, p2) -> (p.getSalary() - p2.getSalary()) )
.collect( toList() );

sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));

min和max方法

比如如果只对最低和最高的薪水感兴趣,比排序后选择第一个/最后一个 更快的是min和max方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
System.out.println("工资最低的 Java programmer:");  
Person pers = javaProgrammers
.stream()
.min((p1, p2) -> (p1.getSalary() - p2.getSalary()))
.get()

System.out.printf("Name: %s %s; Salary: $%,d.", pers.getFirstName(), pers.getLastName(), pers.getSalary())

System.out.println("工资最高的 Java programmer:");
Person person = javaProgrammers
.stream()
.max((p, p2) -> (p.getSalary() - p2.getSalary()))
.get()

System.out.printf("Name: %s %s; Salary: $%,d.", person.getFirstName(), person.getLastName(), person.getSalary())

collect方法合map方法结合

上面的例子中我们已经看到 collect 方法是如何工作的。 结合 map 方法,我们可以使用 collect 方法来将我们的结果集放到一个字符串,一个 Set 或一个TreeSet中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
System.out.println("将 PHP programmers 的 first name 拼接成字符串:");  
String phpDevelopers = phpProgrammers
.stream()
.map(Person::getFirstName)
.collect(joining(" ; ")); // 在进一步的操作中可以作为标记(token)

System.out.println("将 Java programmers 的 first name 存放到 Set:");
Set<String> javaDevFirstName = javaProgrammers
.stream()
.map(Person::getFirstName)
.collect(toSet());

System.out.println("将 Java programmers 的 first name 存放到 TreeSet:");
TreeSet<String> javaDevLastName = javaProgrammers
.stream()
.map(Person::getLastName)
.collect(toCollection(TreeSet::new));

Streams可以是并行的(parallel)

1
2
3
4
5
System.out.println("计算付给 Java programmers 的所有money:");  
int totalSalary = javaProgrammers
.parallelStream()
.mapToInt(p -> p.getSalary())
.sum();

summaryStatistics方法来进行聚合

可以使用summaryStatistics方法获得stream 中元素的各种汇总数据。 接下来,我们可以访问这些方法,比如getMax, getMin, getSum或getAverage:

1
2
3
4
5
6
7
8
9
10
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);  
IntSummaryStatistics stats = numbers
.stream()
.mapToInt((x) -> x)
.summaryStatistics();

System.out.println("List中最大的数字 : " + stats.getMax());
System.out.println("List中最小的数字 : " + stats.getMin());
System.out.println("所有数字的总和 : " + stats.getSum());
System.out.println("所有数字的平均值 : " + stats.getAverage());

去除重复

去除List中重复的String

1
List unique = list.stream().distinct().collect(Collectors.toList());

去除List中重复的对象

1
2
3
4
5
6
7
8
9
10
11
// 根据name去重
List<Person> unique = persons.stream().collect(
Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person::getName))), ArrayList::new)
);

// 根据name,sex两个属性去重
List<Person> unique = persons.stream().collect(
Collectors. collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getName() + ";" + o.getSex()))), ArrayList::new)
);

List转Map

从一个Person对象的List集合,取出id和name组成一个map集合

1
Map<String, String> collect = list.stream().collect(Collectors.toMap(p -> p.getId(), p -> p.getName()));

分组后聚合

谈谈今天工作中遇到的,先说说需求哈,按账户类型分组后,再按收支类型分组,并统计金额和笔数,不明白,怕想不起来是啥是吗,放张图:

看看代码实现吧:

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
List<BeanMap<String, Object>> list = accountChgService.queryUserBills(obj);
// 获取账户分类信息 -> 示例:余额账户:总支出xx笔,xx元;总收入xx笔,xx元...
List<BeanMap> cofferInfos = new ArrayList<>();
// 按账户类型分组
Map<Integer, List<BeanMap<String, Object>>> collect = list.stream().collect(Collectors.groupingBy(beanmap -> beanmap.getIntegerValue("acctType")));
collect.keySet().forEach(acctType -> {
// 按收入类型分组
Map<Integer, List<BeanMap<String, Object>>> agTypeCol = collect.get(acctType).stream().collect(Collectors.groupingBy(beanmap -> beanmap.getIntegerValue("agType")));
agTypeCol.keySet().forEach(agtype -> {
Long count = agTypeCol.get(agtype).stream().count();
Integer inFee = agTypeCol.get(agtype)
.stream()
.mapToInt(beanmap -> beanmap.getIntegerValue("inFee"))
.sum();
Integer outFee = agTypeCol.get(agtype)
.stream()
.mapToInt(beanmap -> beanmap.getIntegerValue("outFee"))
.sum();
BeanMap<String, Object> map = new BeanMap<>();
map.put("acctType",acctType);
map.put("agType",agtype);
map.put("count", count);
map.put("sumInfee",inFee);
map.put("sumOutFee",outFee);
cofferInfos.add(map);
});
});

PS:BeanMap是我自定义的一个继承了HashMap的类,list查询出来的数据是列表要展示的,包括了收入金额(inFee),支出金额(outFee),说白了,就是不想写这么简单又啰嗦的SQL语句了,所以代码来解决一下,这种写法不见得是最优秀的,后续如果理解或者发现了更好的方式,放上来…

待解决问题

在stream内如何使外部变量进行运算,例如:

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
int index = 0;
List<Integer> itgs = Arrays.asList(1, 3, 56, 78, 9);
itgs.forEach(v -> {
if (v == 3) {
index++;
}
});
}

上面会报错,要求index必须是final修饰,这种情况暂时找不到太好的解决办法,网上说可以定义为全局变量来解决,但这我不认为是好的,毕竟不能因为用了stream而改变代码结构。

先总结这些,待续…


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!