- Lambda表达式
- 实例
- 优化方式一:策略设计模式
- 优化方式二:匿名内部类
- 优化方式三:Lambda表达式
- 优化方式四:Stream API
- Lambda表达式语法
- 函数式接口
- 自定义函数式接口
- 作为参数传递Lambda表达式
- Java 内置四大核心函数式接口
- 方法引用与构造器引用
- Stream API
- 创建Stream
- Stream 的中间操作
- Stream的终止操作
- 接口中的默认方法与静态方法
- 新时间日期API
- 其他新特性
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
//原来的匿名内部类 @Test public void test1(){ Comparatorcomparator = new Comparator () { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1,o2); } }; TreeSet ts = new TreeSet<>(comparator); }
//Lambda表达式 @Test public void test2(){ Comparator实例comparator = (x,y) -> Integer.compare(x,y); TreeSet ts = new TreeSet<>(comparator); }
员工实体类
package com.xxxx.lln; public class Employee { private String name; private int age; private double salary; public Employee() { } public Employee(String name, int age, double salary) { this.name = name; this.age = age; this.salary = salary; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } @Override public String toString() { return "Employee{" + "name='" + name + ''' + ", age=" + age + ", salary=" + salary + '}'; } }
定义员工集合
//员工集合 Listemployees = Arrays.asList( new Employee("张三",18,9999.99), new Employee("李四",38,6666.66), new Employee("王五",35,5555.55), new Employee("赵六",13,3333.33), new Employee("田七",26,8888.88) );
需求1:获取年龄大于35的员工信息
//过滤 public ListfilterEmployees(List list){ List emps =new ArrayList<>(); for (Employee emp : list) { if (emp.getAge()>=35){ emps.add(emp); } } return emps; } //调用 @Test public void test3(){ List list = filterEmployees(employees); for(Employee emp : list){ System.out.println(emp.toString()); } }
需求2:获取工资大于5000的员工信息
//过滤 public ListfilterEmployees2(List list){ List emps =new ArrayList<>(); for (Employee emp : list) { if (emp.getSalary()>=5000){//只改变了方法名和这一句 emps.add(emp); } } return emps; } //调用 ......
每次都要写过滤函数,太麻烦了
优化方式一:策略设计模式定义一个接口类
package com.xxxx.lln; public interface MyPredicate{ public boolean test(T t); }
定义过滤的方法函数
//过滤员工,需要传入员工集合和过滤条件的接口类 public ListfilterEmployee(List list , MyPredicate mp){ List emps = new ArrayList<>(); for (Employee employee:list) { if (mp.test(employee)){ emps.add(employee); } } return emps; }
需要的时候去实现接口类
package com.xxxx.lln; public class FilterEmployeeByAge implements MyPredicate{ @Override public boolean test(Employee employee) { return employee.getAge()>=35; } }
public class FilterEmployeeBySalary implements MyPredicate{ @Override public boolean test(Employee employee) { return employee.getSalary()>=5000; } }
调用:
@Test public void test4(){ List优化方式二:匿名内部类list = filterEmployee(employees,new FilterEmployeeByAge()); for (Employee employee : list) { System.out.println(employee); } System.out.println("------------------------------------"); List list2 = filterEmployee(employees,new FilterEmployeeBySalary()); for (Employee employee : list2) { System.out.println(employee); } }
@Test public void test5(){ List优化方式三:Lambda表达式list = filterEmployee(employees, new MyPredicate () { @Override public boolean test(Employee employee) { return employee.getSalary()>=5000; } }); for (Employee employee : list){ System.out.println(employee); } }
//语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符 ,读作(goes to) @Test public void test6(){ //仍需要调用过滤的那个接口 List优化方式四:Stream APIlist = filterEmployee(employees,(e) -> e.getSalary() >= 5000); list.forEach(System.out::println); }
@Test public void test7(){ //不需要用上面的接口 employees.stream() .filter((e) -> e.getSalary() >= 5000) .limit(2)//取前两个 .forEach(System.out::println); System.out.println("---------------------------"); //把所有名字拿出来 employees.stream() .map(Employee::getName) .forEach(System.out::println); }Lambda表达式语法
Lambda 表达式在Java 语言中引入了一个新的语法元 素和操作符。这个操作符为 -> , 该操作符被称 为 Lambda 操作符或剪头操作符。
它将 Lambda 分为 两个部分:
左侧:指定了 Lambda 表达式需要的所有参数
右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。
这里给出六个接口,后文的全部操作都利用这六个接口来进行阐述。
@FunctionalInterface public interface NoReturnMultiParam { void method(int a, int b); } @FunctionalInterface public interface NoReturnNoParam { void method(); } @FunctionalInterface public interface NoReturnOneParam { void method(int a); } @FunctionalInterface public interface ReturnMultiParam { int method(int a, int b); } @FunctionalInterface public interface ReturnNoParam { int method(); } @FunctionalInterface public interface ReturnOneParam { int method(int a); }
语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符 ,读作(goes to)。
import lambda.interfaces.*; public class Test1 { public static void main(String[] args) { //无参无返回 NoReturnNoParam noReturnNoParam = () -> { System.out.println("NoReturnNoParam"); }; noReturnNoParam.method(); //一个参数无返回 NoReturnOneParam noReturnOneParam = (int a) -> { System.out.println("NoReturnOneParam param:" + a); }; noReturnOneParam.method(6); //多个参数无返回 NoReturnMultiParam noReturnMultiParam = (int a, int b) -> { System.out.println("NoReturnMultiParam param:" + "{" + a +"," + + b +"}"); }; noReturnMultiParam.method(6, 8); //无参有返回值 ReturnNoParam returnNoParam = () -> { System.out.print("ReturnNoParam"); return 1; }; int res = returnNoParam.method(); System.out.println("return:" + res); //一个参数有返回值 ReturnOneParam returnOneParam = (int a) -> { System.out.println("ReturnOneParam param:" + a); return 1; }; int res2 = returnOneParam.method(6); System.out.println("return:" + res2); //多个参数有返回值 ReturnMultiParam returnMultiParam = (int a, int b) -> { System.out.println("ReturnMultiParam param:" + "{" + a + "," + b +"}"); return 1; }; int res3 = returnMultiParam.method(6, 8); System.out.println("return:" + res3); } }
Lambda 语法简化
我们可以通过观察以下代码来完成代码的进一步简化,写出更加优雅的代码。
import lambda.interfaces.*; public class Test2 { public static void main(String[] args) { //1.简化参数类型,可以不写参数类型,但是必须所有参数都不写 NoReturnMultiParam lamdba1 = (a, b) -> { System.out.println("简化参数类型"); }; lamdba1.method(1, 2); //2.简化参数小括号,如果只有一个参数则可以省略参数小括号 NoReturnOneParam lambda2 = a -> { System.out.println("简化参数小括号"); }; lambda2.method(1); //3.简化方法体大括号,如果方法条只有一条语句,则可以省略方法体大括号 NoReturnNoParam lambda3 = () -> System.out.println("简化方法体大括号"); lambda3.method(); //4.如果方法体只有一条语句,并且是 return 语句,则可以省略方法体大括号 ReturnOneParam lambda4 = a -> a+3; System.out.println(lambda4.method(5)); ReturnMultiParam lambda5 = (a, b) -> a+b; System.out.println(lambda5.method(1, 1)); } }
上述 Lambda 表达式中的参数类型都是由编译器推断 得出的。Lambda 表达式中无需指定类型,程序依然可 以编译,这是因为 javac 根据程序的上下文,在后台 推断出了参数的类型。Lambda 表达式的类型依赖于上 下文环境,是由编译器推断出来的。这就是所谓的 “ 类型推断”。
函数式接口
什么是函数式接口
- 只包含一个抽象方法的接口,称为函数式接口。
- 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
- 我们可以在任意函数式接口上使用 @FunctionalInterface 注解, 这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
@FunctionalInterface public interface MyNumber{ public double getValue(); }
函数式接口中使用泛型:
@FunctionalInterface public interface MyFunction作为参数传递Lambda表达式{ public T getValue(); }
public String toUpperString(MyFunctionm,String s){ return m.getValue(s); } //作为参数传递 String newStr = toUpperString((str)->str.toUpperCase(),"abcdef"); System.out.println(newStr);
需求:对一个数进行运算
@FunctionalInterface public interface MyFunction { public Integer getValue(Integer num); } public Integer operation(Integer num , MyFunction m){ return m.getValue(num); } @Test public void test(){ Integer num = operation(100,x -> x * x); System.out.println(num); System.out.println(operation(200,x -> x+300)); }Java 内置四大核心函数式接口
方法引用与构造器引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!
方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来。
如下三种主要使用情况:
- 对象::实例方法
- 类::静态方法
- 类::实例方法
注意:当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参数(或无参数)时:ClassName::methodName
//对象::实例方法名 //Consumercon = x -> System.out.println(x); Consumer con = System.out::println; con.accept("asdfghjkl"); System.out.println("------------------------------------"); Employee emp = new Employee("lln",18,999); //Supplier supplier = () -> emp.getName(); Supplier supplier = emp::getName; System.out.println(supplier.get()); System.out.println("------------------------------------"); //类::静态方法名 Comparator com = (x,y) -> Integer.compare(x,y); //Comparator com = Integer::compare; System.out.println(com.compare(1,2)); System.out.println("------------------------------------"); //类::实例方法名 //BiPredicate bp = (x,y)->x.equals(y); //规则:第一个参数是方法的调用者,第二个参数是要调用方法的参数时,可以使用类::实例方法名 BiPredicate bp = String::equals;
构造器引用
与函数式接口相结合,自动与函数式接口中方法兼容。 可以把构造器引用赋值给定义的方法,构造器参数列表要与接口中抽象方法的参数列表一致!
ClassName::new
interface ItemCreatorBlankConstruct { Item getItem(); } interface ItemCreatorParamContruct { Item getItem(int id, String name, double price); } public class Exe2 { public static void main(String[] args) { ItemCreatorBlankConstruct creator = () -> new Item(); Item item = creator.getItem(); ItemCreatorBlankConstruct creator2 = Item::new; Item item2 = creator2.getItem(); ItemCreatorParamContruct creator3 = Item::new; Item item3 = creator3.getItem(112, "鼠标", 135.99); } }
数组引用
//数组引用 //Functionfun = (x) -> new String[x]; Function fun = String[]::new; String[] strs = fun.apply(10); System.out.println(strs.length);
Stream API
Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一 个则是 Stream API(java.util.stream.*)。
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对 集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。
也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
流(Stream) 到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,流讲的是计算!”
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
创建Stream
1.可以通过Collection系列集合提供的stream()或parallelStream()
Java8 中的 Collection 接口被扩展,提供了 两个获取流的方法:
- default Stream stream() : 返回一个顺序流
- default Stream parallelStream() : 返回一个并行流
Listlist = new ArrayList<>(); Stream stream1 = list.stream();
2.由数组创建
Java8 中的 Arrays 的静态方法 stream() 可 以获取数组流:
static Stream stream(T[] array): 返回一个流
Employee[] emps = new Employee[10]; Streamstream2 = Arrays.stream(emps);
3.由值创建流
通过Stream类中的静态方法of()
可以使用静态方法 Stream.of(), 通过显示值 创建一个流。它可以接收任意数量的参数。
Streamstream3 = Stream.of("aa","bb","cc");
4.由函数创建流:创建无限流
可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
-
迭代 public static Stream iterate(final T seed, final UnaryOperator f)
-
生成 public static Stream generate(Supplier s) :
//迭代 StreamStream 的中间操作stream4 = Stream.iterate(0,(x) -> x + 2); stream4.limit(10) .forEach(System.out::println); //生成 Stream.generate(()->Math.random()) .limit(5) .forEach(System.out::println);
多个中间操作可以连接起来形成一个流水线,除非流水 线上触发终止操作,否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为“惰性求值”。
//员工集合 Listemployees = Arrays.asList( new Employee("张三",18,9999.99), new Employee("李四",38,6666.66), new Employee("王五",35,5555.55), new Employee("赵六",13,3333.33), new Employee("田七",26,8888.88) ); //内部迭代:迭代操作由Stream API完成 @Test public void test1(){ //中间操作:不会执行任何操作 Stream stream = employees.stream() .filter((e)->e.getAge()>35); //终止操作:一次性执行全部内容,即“惰性求值” stream.forEach(System.out::println); } //外部迭代 @Test public void test2(){ Iterator it = employees.iterator(); while(it.hasNext()){ System.out.println(it.next()); } }
筛选与切片
@Test public void test3(){ employees.stream() .filter((e)->e.getSalary()>5000) .limit(2) .forEach(System.out::println); } //只要迭代到满足条件的两个数据后,后续操作就不进行了
@Test public void test4(){ employees.stream() .filter((e)->e.getSalary()>5000) //跳过前两个满足条件的数据 .skip(2) .forEach(System.out::println); }
去重
@Test public void test5(){ employees.stream() .filter((e)->e.getSalary()>5000) .distinct() .forEach(System.out::println); }
映射
@Test public void test6(){ Listlist = Arrays.asList("aaa","bbb","ccc"); list.stream() .map((str)->str.toUpperCase()) .forEach(System.out::println); System.out.println("--------------------"); //提取名字 employees.stream() .map(Employee::getName) .distinct() .forEach(System.out::println); }
flatMap
@Test public void test7(){ Listlist = Arrays.asList("aaa","bbb","ccc"); // {{a,a,a},{b,b,b},{c,c,c}} // Stream > stream = list.stream() // .map(TestStreamAPI2::filterCharacter); // // stream.forEach((sm)->{ // sm.forEach(System.out::println); // }); System.out.println("-----------------------"); //{a,a,a,b,b,b,c,c,c} Stream sm = list.stream() .flatMap(TestStreamAPI2::filterCharacter); sm.forEach(System.out::println); } public static Stream filterCharacter(String str){ List list = new ArrayList<>(); for (Character ch : str.toCharArray()){ list.add(ch); } return list.stream(); }
排序
Stream的终止操作终端操作会从流的流水线生成结果。其结果可以是任何不是流的 值,例如:List、Integer,甚至是 void 。
查找和匹配
allMatch——检查是否匹配所有元素 传入断定型接口
anyMatch——检查是否至少匹配一个元素 传入断定型接口
noneMatch——检查是否没有匹配的元素 传入断定型接口
findFirst——返回第一个元素
findAny——返回当前流中的任意一个元素
count——返回流中元素的总个数
max——返回流中最大值 传入Comparator接口
min——返回流中最小值 传入Comparator接口
接口中的默认方法与静态方法 新时间日期API 其他新特性