JDK1.8特性简介
- Lambda语法表达式
- 函数式接口
- 方法引用
- Stream API
Lambda语法表达式
lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码
Lambda 优点:简介代码
//匿名内部类
Comparator<Integer> cpt = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
TreeSet<Integer> set = new TreeSet<>(cpt);
System.out.println("=========================");
//使用lambda表达式
Comparator<Integer> cpt2 = (x,y) -> Integer.compare(x,y);
TreeSet<Integer> set2 = new TreeSet<>(cpt2);
一行代码极大减少了代码量!
而在浏览商品时,我们时常会进行筛选,例如要选颜色为红色的、价格小于8000千的….
// 筛选颜色为红色
public List<Product> filterProductByColor(List<Product> list){
List<Product> prods = new ArrayList<>();
for (Product product : list){
if ("红色".equals(product.getColor())){
prods.add(product);
}
}
return prods;
}
// 筛选价格小于8千的
public List<Product> filterProductByPrice(List<Product> list){
List<Product> prods = new ArrayList<>();
for (Product product : list){
if (product.getPrice() < 8000){
prods.add(product);
}
}
return prods;
}
我们发现实际上这些过滤方法的核心就只有if语句中的条件判断,其他均为模版代码,每次变更一下需求,都需要新增一个方法,然后复制黏贴,假设这个过滤方法有几百行,那么这样的做法难免笨拙了一点。如何进行优化呢?
优化一:使用匿名内部类
定义过滤方法:
public List<Product> filterProductByPredicate(List<Product> list,MyPredicate<Product> mp){
List<Product> prods = new ArrayList<>();
for (Product prod : list){
if (mp.test(prod)){
prods.add(prod);
}
}
return prods;
}
调用过滤方法的时候:
// 按价格过滤
public void test2(){
filterProductByPredicate(proList, new MyPredicate<Product>() {
@Override
public boolean test(Product product) {
return product.getPrice() < 8000;
}
});
}
// 按颜色过滤
public void test3(){
filterProductByPredicate(proList, new MyPredicate<Product>() {
@Override
public boolean test(Product product) {
return "红色".equals(product.getColor());
}
});
}
使用匿名内部类,就不需要每次都新建一个实现类,直接在方法内部实现。
优化二:使用lambda表达式
定义过滤方法:
public List<Product> filterProductByPredicate(List<Product> list,MyPredicate<Product> mp){
List<Product> prods = new ArrayList<>();
for (Product prod : list){
if (mp.test(prod)){
prods.add(prod);
}
}
return prods;
}
使用lambda表达式进行过滤:
@Test
public void test4(){
List<Product> products = filterProductByPredicate(proList, (p) -> p.getPrice() < 8000);
for (Product pro : products){
System.out.println(pro);
}
}
Lambda表达式的语法总结: () -> ();
前置 | 语法 |
---|---|
无参数无返回值 | () -> System.out.println(“Hello WOrld”) |
有一个参数无返回值 | (x) -> System.out.println(x) |
有且只有一个参数无返回值 | x -> System.out.println(x) |
有多个参数,有返回值,有多条lambda体语句 | (x,y) -> {System.out.println(“xxx”);return xxxx;}; |
有多个参数,有返回值,只有一条lambda体语句 | (x,y) -> xxxx |
口诀:左右遇一省括号,左侧推断类型省
注:当一个接口中存在多个抽象方法时,如果使用lambda表达式,并不能智能匹配对应的抽象方法,因此引入了函数式接口的概念
函数式接口
函数式接口的提出是为了给Lambda表达式的使用提供更好的支持。
什么是函数式接口?
简单来说就是只定义了一个抽象方法的接口(Object类的public方法除外),就是函数式接口,注解:@FunctionalInterface
常见的四大函数式接口
- Consumer 《T》:消费型接口,有参无返回值
@Test
public void test(){
changeStr("hello",(str) -> System.out.println(str));
}
/**
* Consumer<T> 消费型接口
* @param str
* @param con
*/
public void changeStr(String str, Consumer<String> con){
con.accept(str);
}
- Supplier 《T》:供给型接口,无参有返回值
@Test
public void test2(){
String value = getValue(() -> "hello");
System.out.println(value);
}
/**
* Supplier<T> 供给型接口
* @param sup
* @return
*/
public String getValue(Supplier<String> sup){
return sup.get();
}
- Function 《T,R》::函数式接口,有参有返回值
@Test
public void test3(){
Long result = changeNum(100L, (x) -> x + 200L);
System.out.println(result);
}
/**
* Function<T,R> 函数式接口
* @param num
* @param fun
* @return
*/
public Long changeNum(Long num, Function<Long, Long> fun){
return fun.apply(num);
}
- Predicate《T》: 断言型接口,有参有返回值,返回值是boolean类型
public void test4(){
boolean result = changeBoolean("hello", (str) -> str.length() > 5);
System.out.println(result);
}
/**
* Predicate<T> 断言型接口
* @param str
* @param pre
* @return
*/
public boolean changeBoolean(String str, Predicate<String> pre){
return pre.test(str);
}
总结:函数式接口的提出是为了让我们更加方便的使用lambda表达式,不需要自己再手动创建一个函数式接口
方法引用
若lambda体中的内容有方法已经实现了,那么可以使用“方法引用”
也可以理解为方法引用是lambda表达式的另外一种表现形式并且其语法比lambda表达式更加简单
(a) 方法引用
三种表现形式:
- 对象::实例方法名
- 类::静态方法名
- 类::实例方法名 (lambda参数列表中第一个参数是实例方法的调用 者,第二个参数是实例方法的参数时可用)
public void test() {
/**
*注意:
* 1.lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
* 2.若lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method
*
*/
Consumer<Integer> con = (x) -> System.out.println(x);
con.accept(100);
// 方法引用-对象::实例方法
Consumer<Integer> con2 = System.out::println;
con2.accept(200);
// 方法引用-类名::静态方法名
BiFunction<Integer, Integer, Integer> biFun = (x, y) -> Integer.compare(x, y);
BiFunction<Integer, Integer, Integer> biFun2 = Integer::compare;
Integer result = biFun2.apply(100, 200);
// 方法引用-类名::实例方法名
BiFunction<String, String, Boolean> fun1 = (str1, str2) -> str1.equals(str2);
BiFunction<String, String, Boolean> fun2 = String::equals;
Boolean result2 = fun2.apply("hello", "world");
System.out.println(result2);
}
(b)构造器引用
格式:ClassName::new
public void test2() {
// 构造方法引用 类名::new
Supplier<Employee> sup = () -> new Employee();
System.out.println(sup.get());
Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());
// 构造方法引用 类名::new (带一个参数)
Function<Integer, Employee> fun = (x) -> new Employee(x);
Function<Integer, Employee> fun2 = Employee::new;
System.out.println(fun2.apply(100));
}
©数组引用
格式:Type[]::new
public void test(){
// 数组引用
Function<Integer, String[]> fun = (x) -> new String[x];
Function<Integer, String[]> fun2 = String[]::new;
String[] strArray = fun2.apply(10);
Arrays.stream(strArray).forEach(System.out::println);
}
Stream API
Stream操作的三个步骤
- 创建stream
- 中间操作(过滤、map)
- 终止操作
stream的创建:
// 1,校验通过Collection 系列集合提供的stream()或者paralleStream()
List<String> list = new ArrayList<>();
Strean<String> stream1 = list.stream();
// 2.通过Arrays的静态方法stream()获取数组流
String[] str = new String[10];
Stream<String> stream2 = Arrays.stream(str);
// 3.通过Stream类中的静态方法of
Stream<String> stream3 = Stream.of("aa","bb","cc");
// 4.创建无限流
// 迭代
Stream<Integer> stream4 = Stream.iterate(0,(x) -> x+2);
//生成
Stream.generate(() ->Math.random());
Stream的中间操作:
/**
* 筛选 过滤 去重
*/
emps.stream()
.filter(e -> e.getAge() > 10)
.limit(4)
.skip(4)
// 需要流中的元素重写hashCode和equals方法
.distinct()
.forEach(System.out::println);
/**
* 生成新的流 通过map映射
*/
emps.stream()
.map((e) -> e.getAge())
.forEach(System.out::println);
/**
* 自然排序 定制排序
*/
emps.stream()
.sorted((e1 ,e2) -> {
if (e1.getAge().equals(e2.getAge())){
return e1.getName().compareTo(e2.getName());
} else{
return e1.getAge().compareTo(e2.getAge());
}
})
.forEach(System.out::println);
Stream的终止操作:
/**
* 查找和匹配
* allMatch-检查是否匹配所有元素
* anyMatch-检查是否至少匹配一个元素
* noneMatch-检查是否没有匹配所有元素
* findFirst-返回第一个元素
* findAny-返回当前流中的任意元素
* count-返回流中元素的总个数
* max-返回流中最大值
* min-返回流中最小值
*/
/**
* 检查是否匹配元素
*/
boolean b1 = emps.stream()
.allMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
System.out.println(b1);
boolean b2 = emps.stream()
.anyMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
System.out.println(b2);
boolean b3 = emps.stream()
.noneMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
System.out.println(b3);
Optional<Employee> opt = emps.stream()
.findFirst();
System.out.println(opt.get());
// 并行流
Optional<Employee> opt2 = emps.parallelStream()
.findAny();
System.out.println(opt2.get());
long count = emps.stream()
.count();
System.out.println(count);
Optional<Employee> max = emps.stream()
.max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(max.get());
Optional<Employee> min = emps.stream()
.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(min.get());
还有功能比较强大的两个终止操作 reduce和collect
reduce操作: reduce:(T identity,BinaryOperator)/reduce(BinaryOperator)-可以将流中元素反复结合起来,得到一个值
/**
* reduce :规约操作
*/
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer count2 = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(count2);
Optional<Double> sum = emps.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(sum);
collect操作:Collect-将流转换为其他形式,接收一个Collection接口的实现,用于给Stream中元素做汇总的方法
/**
* collect:收集操作
*/
List<Integer> ageList = emps.stream()
.map(Employee::getAge)
.collect(Collectors.toList());
ageList.stream().forEach(System.out::println);