Java8 Lambda表达式(一)
一、应用场景引入
首先编写对应的实体类模拟实际业务:
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Employee {
private String name;
private Integer age;
private double salary;
}
具体数据:
List<Employee> employees = Arrays.asList(
new Employee("张三", 18, 6999.9, Employee.Status.FREE),
new Employee("李四", 38, 3999.9, Employee.Status.BUSY),
new Employee("王五", 50, 5999.9, Employee.Status.VOCATION),
new Employee("赵六", 19, 2999.9, Employee.Status.FREE),
new Employee("田七", 18, 4999.9, Employee.Status.BUSY)
);
此时我们需要得到年龄>=35的所有员工,则可以:
public List<Employee> filterEmployees(List<Employee> employees) {
List<Employee> res = new ArrayList<>();
for (Employee employee : employees) {
if (employee.getAge()>=35){
res.add(employee);
}
}
return res;
}
@Test
public void t3() {
List<Employee> res = filterEmployees(employees);
for (Employee employee : res) {
System.out.println(employee);
}
}
但这样可扩展性不好,当我们需要更改过滤条件时则需要新写一个过滤函数,其中有很多相同的代码。
优化一:使用策略模式
先编写一个过滤策略接口:
public interface MyPredicate<t> {
boolean condition(T t);
}
对应的用于过滤年龄的实现类FilterEmployeeByAge:
public class FilterEmployeeByAge implements MyPredicate<Employee> {
@Override
public boolean condition(Employee employee) {
return employee.getAge() >= 35;
}
}
然后再编写根据策略进行过滤的函数:
public List<employee> filterEmployees(List<Employee> employees, MyPredicate<Employee> predicate) {
List<Employee> res = new ArrayList<>();
for (Employee employee : employees) {
if (predicate.condition(employee)){
res.add(employee);
}
}
return res;
}
这样我们即可根据不同的策略进行过滤了,如此时我们希望得到年龄>=35的所有员工:
// 策略模式
@Test
public void t4() {
List<Employee> employees = filterEmployees(this.employees, new FilterEmployeeByAge());
for (Employee employee : employees) {
System.out.println(employee);
}
}
若我们希望进行扩展,此时希望得到工资>=3000的所有员工,则可以再编写一个策略实现类,然后再进行相应的策略选择:
// FilterEmployeeBySalary.java
public class FilterEmployeeBySalary implements MyPredicate<Employee> {
@Override
public boolean condition(Employee employee) {
return employee.getSalary() >= 3000;
}
}
// MyTest.java
@Test
public void t5() {
List<Employee> employees = filterEmployees(this.employees, new FilterEmployeeBySalary());
for (Employee employee : employees) {
System.out.println(employee);
}
}
但通过这种方法还是比较麻烦,每定义一种过滤条件都需要新建一个类,这时我们可以进一步优化。
优化二:使用匿名内部类
// 匿名内部类
@Test
public void t6() {
List<Employee> employees = filterEmployees(this.employees, new MyPredicate<Employee>() {
@Override
public boolean condition(Employee employee) {
return employee.getAge() > 40;
}
});
for (Employee employee : employees) {
System.out.println(employee);
}
}
这时,我们需要什么样的条件只需要在匿名内部类中进行编写即可,不需要编写新的类,但匿名内部类的形式还是显得不够简洁,可读性不够高。
优化三:使用Lambda表达式
// lambda表达式
@Test
public void t7() {
List<Employee> employees = filterEmployees(this.employees, employee -> employee.getAge() > 30);
employees.forEach(System.out::println);
}
可以看到代码简洁了很多,而且可读性也变高了。
优化四:使用Stream API
在上一步优化中,我们仍需手动定义过滤使用的接口和自定义的过滤函数,那么JDK中有没有直接可以“拿来”的工局呢?Stream API就是JDK中提供的专门用来进行过滤操作的工具,使用方式如下:
// Stream API
@Test
public void t8() {
employees.stream()
.filter(employee -> employee.getSalary() >= 3000)
.forEach(System.out::println);
System.out.println("---------------");
// 得到其中所有的字段
employees.stream()
.map(Employee::getName)
.forEach(System.out::println);
}
运行结果:
Employee(name=李四, age=38, salary=3999.9)
Employee(name=王五, age=50, salary=5999.9)
Employee(name=田七, age=16, salary=4999.9)
---------------
张三
李四
王五
赵六
田七
二、Lambda运算符和对应语法

语法格式
语法格式一:无参数,无返回值
() -> System.out.println("hello world");
@Test
public void t1() {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello world");
}
};
r.run();
System.out.println("-------------------------");
Runnable r1 = () -> System.out.println("Hello Lambda");
r1.run();
}
语法格式二:有一个参数,而且无返回值
(x) -> System.out.println(x);
@Test
public void t2() {
Consumer<String> con = (s) -> System.out.println(s);
con.accept("我大尚硅谷威武");
}
语法格式三:若只有一个参数,小括号可以省略不写
x -> System.out.println(x);
语法格式四:有两个以上的参数,有返回值,且Lambda体中有多条语句
Comparator<Integer> comparator1 = (o1, o2) -> {
System.out.println("函数式接口");
return Integer.compare(o1, o2);
};
@Test
public void t3() {
// 使用匿名内部类
Comparator<Integer> comparator = new Comparator<integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
// 使用Lambda表达式
Comparator<Integer> comparator1 = (o1, o2) -> {
System.out.println("函数式接口");
return Integer.compare(o1, o2);
};
}
语法格式五:若Lambda体中只有一条语句,return和大括号都可以省略
Comparator<Integer> comparator = (o1, o2) -> o1 - o2;
@Test
public void t4() {
Comparator<Integer> comparator = (o1, o2) -> o1 - o2;
int compare = comparator.compare(6, 2);
System.out.println(compare);
}
语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,原:
Comparator<Integer> comparator = (Integer o1, Integer o2) -> o1 - o2;
口诀:
左右遇一括号省
左侧类型推断省
Lambda表达式需要“函数式接口”的支持
函数式接口:接口中只有一个抽象方法的接口,可以使用注解@FunctionalInterface进行修饰
三、简单应用
1.调用Collections.sort() 方法,通过定制排序比较两个Employee (先按年龄比,年龄相同按Salary比),使用Lambda作为参数传递。
准备数据:
List<Employee> employees = Arrays.asList(
new Employee("张三", 18, 6999.9),
new Employee("李四", 38, 3999.9),
new Employee("王五", 50, 5999.9),
new Employee("赵六", 19, 2999.9),
new Employee("田七", 18, 4999.9)
);
客户端:
@Test
public void t1() {
employees.sort((o1, o2) -> {
if (o1.getAge() != o2.getAge()) {
return Integer.compare(o1.getAge(), o2.getAge());
} else {
return Double.compare(o1.getSalary(), o2.getSalary());
}
});
employees.forEach(System.out::println);
}
2.①声明函数式接口,接口中声明抽象方法,public String getValue(String str);
②声明类TestLambda ,类中编写方法使用接口作为参数,将一个字符串转换成大写,
并作为方法的返回值。
函数式接口
@FunctionalInterface
public interface MyFunction {
public String getValue(String str);
}
客户端:
public String strHandler(String str, MyFunction mf) {
return mf.getValue(str);
}
@Test
public void test2() {
String res = strHandler("abcdefg", str -> str.toUpperCase());
System.out.println(res);
}
3.①声明一个带两个泛型的函数式接口,泛型类型为<t,r> T为参数,R为返回值。
②接口中声明对应抽象方法。
③在TestLambda 类中声明方法,使用接口作为参数,计算两个long 型参数的和。
④再计算两个long 型参数的乘积。
函数式接口
@FunctionalInterface
public interface MyFunction2<T> {
public R getValue(T t1, T t2);
}
客户端:
public void op(Long l1, Long l2, MyFunction2<Long, Long> mf) {
System.out.println(mf.getValue(l1, l2));
}
@Test
public void t3() {
op(100L, 200L, (t1, t2) -> t1 + t2);
op(100L, 200L, (t1, t2) -> t1 * t2);
}
Java8 Lambda表达式(一)的更多相关文章
- java8 Lambda表达式的新手上车指南(1)
背景 java9的一再推迟发布,似乎让我们恍然想起离发布java8已经过去了三年之久,java8应该算的上java语言在历代版本中变化最大的一个版本了,最大的新特性应该算得上是增加了lambda表达式 ...
- java8 Lambda表达式的新手上车指南(1)--基础语法和函数式接口
背景 java9的一再推迟发布,似乎让我们恍然想起离发布java8已经过去了三年之久,java8应该算的上java语言在历代版本中变化最大的一个版本了,最大的新特性应该算得上是增加了lambda表达式 ...
- Java8 Lambda表达式详解手册及实例
先贩卖一下焦虑,Java8发于2014年3月18日,距离现在已经快6年了,如果你对Java8的新特性还没有应用,甚至还一无所知,那你真得关注公众号"程序新视界",好好系列的学习一下 ...
- Java8 Lambda表达式、函数式接口和方法引用
目录 Java8 Lambda表达式和函数式接口 Lambda表达式 Lambda的使用 函数式接口FunctionalInterface Java内置四大核心函数式接口 方法引用 构造器引用 Jav ...
- 【Java学习笔记之三十一】详解Java8 lambda表达式
Java 8 发布日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Java 8之前 ...
- java8 快速入门 lambda表达式 Java8 lambda表达式10个示例
本文由 ImportNew - lemeilleur 翻译自 javarevisited.欢迎加入翻译小组.转载请见文末要求. Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发 ...
- Java8 lambda表达式10个示例
Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Ja ...
- Java8 lambda表达式语法 1
本文主要记录自己学习Java8的历程,方便大家一起探讨和自己的备忘.因为本人也是刚刚开始学习Java8,所以文中肯定有错误和理解偏差的地方,希望大家帮忙指出,我会持续修改和优化.本文是该系列的第一篇, ...
- java8 Lambda表达式的10个例子(转)
原文:http://jobar.iteye.com/blog/2023477 Java8中Lambda表达式的10个例子 例1 用Lambda表达式实现Runnable接口 Java代码 收藏代码// ...
随机推荐
- Linux环境下安装配置vsftpd服务(三种认证模式)
一.FTP简介 文件传输协议(英文:File Transfer Protocol,缩写:FTP)是用于在网络上进行文件传输的一套标准协议.它工作于网络传输协议的应用层,使用客户/服务器模式,主要是用来 ...
- vue项目中一些标签直接放在<template>下会报错Failed to compile with 1 errors
原因是a标签button以及element-ui的组件不能直接放在<template>下,需要先有一个div,其他标签要放在div下
- Unity3D-UI--Layout组件
Layout组件 自动排版 Layout Group Vertical Layout Group 垂直布局 [垂直布局组]组件将其子布局元素彼此重叠.它们的高度由各自的最小高度,首选高度和柔性高度决定 ...
- centos使用yum安装docker
1.先决条件 要安装 Docker 引擎,您需要 CentOS 7 或 8 的维护版本.不支持或测试存档版本. 该centos-extras库必须启用.默认情况下启用此存储库,但如果您已禁用它,则需要 ...
- Redis在linux系统中的优化
通常来看,Redis开发和运维人员更加关注的是Redis本身的一些配置优化,例如AOF和RDB的配置优化.数据结构的配置优化等,但是对于操作系统是否需要针对Redis做一些配置优化不甚了解或者不太关心 ...
- Mongo集群搭建
1.集群角色及架构 在搭建集群之前,需要首先了解几个概念:路由,分片.副本集.配置服务器等 mongos,数据库集群请求的入口,所有的请求都通过mongos进行协调,不需要在应用程序添加一个路由选择器 ...
- Blazor Server 和 WebAssembly 应用程序入门指南
翻译自 Waqas Anwar 2021年3月12日的文章 <A Beginner's Guide To Blazor Server and WebAssembly Applications&g ...
- hdu 1540 Tunnel Warfare 线段树 区间合并
题意: 三个操作符 D x:摧毁第x个隧道 R x:修复上一个被摧毁的隧道,将摧毁的隧道入栈,修复就出栈 Q x:查询x所在的最长未摧毁隧道的区间长度. 1.如果当前区间全是未摧毁隧道,返回长度 2. ...
- Html:id,name,class之间的有什么区别?
name 一个name可以同时对应多个控件,比如checkbox和radio,主要用于获取提交表单的某表单域信息, 作为可与服务器交互数据的HTML元素的服务器端的标示,比如input.select. ...
- SpringCloud:Eureka 配置心跳机制
Server服务端 server: port: 8761 eureka: client: #实例是否在eureka服务器上注册自己的信息以提供其他服务发现,默认为true re ...