jdk8的新特性
Lambda表达式
StreamAPI
支持并行
Nashorn引擎:jvm中可以运行js
Optional类:减少空指针异常
接口中可以有静态和默认方法
新日期
lambda表达式
一、理解
Lambda表达式 是满足一定条件的匿名内部类的代替,也就是一段可以传递的代码。
条件:匿名内部类实现的接口只有一个抽象方法
method(()->System.out.println());
等价于
method(Runnable s){
public void run(){
System.out.println();
}
}
二、好处
语法更加简洁,更加紧凑,使JAVA语言的表达能力得到了提升!
三、应用场景
1、直接作为接口的实例出现,赋值给接口
Runnable r = ()->System.out.println();
2、作为实参传递给方法
method(()->System.out.println());
四、语法
由三部分组成:
(参数列表)->{抽象方法的实现体或Lambda体}
注意:
1.参数列表中的参数类型可以省略
2.参数列表中如果仅仅有一个参数,则小括号可以省略
3.Lambda体中只有一句话,大括号可以省略
4.Lambda体中仅有的一句话是返回语句,则return关键字也可以省略.
函数式接口
只有一个抽象方法的接口,就称为函数式接口,比如:Runnable、Comparator,我们可以在接口上使用 @FunctionalInterface注解,检查它是否是一个函数式接口
java内置核心的四大函数式接口
消费型接口Consumer<T>
void accept(T t)
供给型接口Supplier<T>
T get()
函数型接口Function<T,R>
R apply(T t)
断定型接口Predicate<T>
boolean test(T t)
方法引用
一、理解:
方法引用属于Lambda表达式的一种,相当于满足一定要求的Lambda表达式,也可以作为函数式接口的实例
二、应用场景:
* 1、作为实例赋值给函数式接口,相当于函数式接口的对象
* 2、作为实参传递给形参为函数式接口类的方法
三、要求:
1、Lambda体的实现里面仅仅只有一句话,而且是方法调用
2、要调用的方法的参数列表和返回类型 必须和接口的抽象方法的参数类别和返回类型一致!
3、备注:如果要调用的方法的调用者正好为抽象方法的第一个参数,其他参数和返回类型一致,这个时候可以使用类名::普通方法 形式!
四、语法:
类名或对象::方法名
分类:
对象::普通方法名
类名::静态方法名
类名::普通方法名 ☆
构造器引用
一、理解:
属于Lambda表达式的一种,作为函数式接口的实例出现的
满足一定要求的Lambda表达式可以改进成构造器引用!
二、语法:
类型::new
三、要求:
①Lambda中仅仅只有一句话
②lambda体中仅有的一句话为 返回一个new出来的对象(通过构造器创建对象)
③抽象方法和调用的构造器的参数列表以及返回一致
数组引用
一、理解:
属于Lambda表达式的一种,作为函数式接口的实例出现的
满足一定要求的Lambda表达式可以改进成数组引用!
二、语法:
数组类型[]::new
三、要求:
①Lambda中仅仅只有一句话
②Lambda体仅有的一句话为 返回一个new出来的数组
③抽象方法的参数正好为数组的长度
总结
匿名内部类>Lambda表达式>方法引用|构造器引用|数组引用
相同点:都是作为接口的实例出现!
不同点:
* 能用方法引用|构造器引用|数组引用 肯定能用 Lambda表达式 代替
* 能用Lambda表达式不一定能用方法引用|构造器引用|数组引用,必须满足一些要求!
* 能用 Lambda表达式 肯定能用 匿名内部类代替
* 能用匿名内部类的不一定能用Lambda表达式,必须满足一个要求:接口为函数式接口!
Stream API
特点
①Stream 不是集合,自己不会存储元素,集合将的是数据,Stream讲的是计算。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。必须搞清楚有哪些数据才能往下执行,这意味着他们会等到需要结果的时候才执行。
④Stream只能“消费”一次,如果想继续做其他操作,需要重新获取stream对象
④更像一个高级的iterator,单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,但是可以并行化数据! \(^o^)/
使用步骤
* 1、开始操作:获取Stream对象,创建一个stream对象指向一个具体的数据源
* 2、中间操作:处理,对数据源进行处理,返回一个stream类型新结果
* 3、终止操作:最后要一个结果
获取stream对象:
方式一:根据集合获取stream对象
方式二:根据数组获取stream对象
方式三:分句一组序列值获取stream对象
方式四:创建无限流,根据一个规则获取stream对象
public class TestStreamStart {
//方式一:根据集合获取Stream对象 (数据源:集合) ★
@Test
public void test1() {
List<Employee> list = EmployeeData.getEmployees();
//获取串行流对象
Stream<Employee> stream = list.stream();
//使用匿名内部类:
// stream.forEach(new Consumer<Employee>(){
// @Override
// public void accept(Employee t) {
// System.out.println(t);
// }
// });
stream.forEach(System.out::println);
}
//方式二:根据数组获取Stream对象 (数据源:数组)
@Test
public void test2() {
String[] arr = {"john","lucy","lily"};
Arrays.stream(arr).forEach(System.out::println);
}
// 方式三:根据一组序列值获取Stream对象(数据源:一组值)
@Test
public void test3() {
Stream<Character> of = Stream.of('a','b','c','d');
of.forEach(System.out::println);
}
//方式四:生成无限流
@Test
public void test4() {
//匿名内部类
// Stream<Double> generate = Stream.generate(new Supplier<Double>(){
// @Override
// public Double get() {
// return Math.random();
// }
// });
//Lambda
// Stream<Double> generate2 = Stream.generate(()->Math.random());
//方法引用
Stream<Double> generate3 = Stream.generate(Math::random);
generate3.forEach(System.out::println);
}
}
Stream 中间操作
- ①惰性求值。 除非进行终止操作,否则前面的中间操作不会执行任何处理
- ②中间操作执行终止操作后,属于“一次性消费”,不能再继续执行终止操作
- ③多个中间操作可以形成流水线操作
常见方法:
筛选与切片:
filter(Predicate p)——接收 Lambda , 从流中排除某些元素。
limit(n)——截断流,使其元素不超过给定数量。
skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
distinct()——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
映射:
map(Function<元素类型,R>)——映射,根据元素本身一一映射成新的元素
flatMap(Function<元素类型,Stream>)——映射
排序:
sorted()——自然排序
sorted(Comparator)——定制排序
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;
import org.junit.Before;
import org.junit.Test;
/**
* 此类用于演示Stream的中间操作
*/
public class TestStreamMiddle {
List<Employee> list;
@Before
public void before(){
list = EmployeeData.getEmployees();
}
//筛选与切片
@Test
public void test1() {
//1.获取Stream对象
Stream<Employee> stream = list.stream();
//2.中间操作
//filter:根据条件过滤
Stream<Employee> filter = stream.filter(t->t.getAge()>30);
//limit:用于限制最大元素个数
Stream<Employee> limit = filter.limit(50);
//skip:跳过指定的个数
Stream<Employee> skip = limit.skip(3);
//distinct:去重
Stream<Employee> distinct = skip.distinct();
//3.终止操作
distinct.forEach(System.out::println);
}
//测试:映射
@Test
public void test2() {
//1.获取Stream对象
Stream<Employee> stream = list.stream();
//2.中间操作
//map:映射
// Stream<String> map = stream.map(new Function<Employee,String>(){
// @Override
// public String apply(Employee t) {
// return t.getName();
// }});
//Stream<String> map = stream.map(Employee::getName);
//flatMap:映射
//使用匿名内部类:
// stream.flatMap(new Function<Employee,Stream<Object>>(){
// @Override
// public Stream<Object> apply(Employee t) {
// return TestStreamMiddle.employeeToStream(t);
// }
// });
//使用方法引用
Stream<Object> flatMap = stream.flatMap(TestStreamMiddle::employeeToStream);
//3.终止操作
flatMap.forEach(System.out::println);
}
//测试排序
@Test
public void test3() {
//1.获取stream对象
Stream<Employee> stream = list.stream();
//2.中间操作
//sorted():根据元素本身的比较性实现自然排序
// Stream<Employee> sorted = stream.sorted();
//sorted(comparator):根据比较器实现定制排序
//进阶1:使用匿名内部类
// stream.sorted(new Comparator<Employee>(){
// @Override
// public int compare(Employee o1, Employee o2) {
// return Double.compare(o2.getSalary(), o1.getSalary());
// }
// });
//进阶2:使用Lambda
Stream<Employee> sorted2 = stream.sorted((o1,o2)->Double.compare(o2.getSalary(), o1.getSalary()));
//3.终止操作
sorted2.forEach(System.out::println);
}
//自定义规则实现:将员工转换成Stream类型
public static Stream<Object> employeeToStream(Employee e){
Stream<Object> of = Stream.of(e.getName(),e.getAge(),e.getSalary(),e.getSex());
return of;
}
//案例1:筛选与切片
//过滤年龄>30&&性别=男&工资>100000
@Test
public void exec1() {
list.stream().
filter(t->t.getAge()>30).
filter(t->t.getSalary()>100000).
filter(t->t.getSex()=='男').
forEach(System.out::println);
}
//案例2:筛选与切片
//过滤:年龄>30的员工中的 第2条——第4条数据
@Test
public void exec2() {
list.stream().filter(t->t.getAge()>30).skip(1).limit(3).forEach(System.out::println);
}
//案例3:映射
//过滤:员工中包含字符“赵”的姓名
@Test
public void exec3() {
list.stream().map(Employee::getName).filter(s->s.contains("赵")).distinct().forEach(System.out::println);;
}
}
stream终止操作:
常见方法:
* count:统计个数
* max(Comparator):求最大值
* min(Comparator):求最小值
* forEach(Consumer):内部迭代
* reduce(BinaryOperator<T>):归约、合并
import java.util.List;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.stream.Stream;
import org.junit.Before;
import org.junit.Test;
/**
* 此类用于演示Stream的终止操作
*/
public class TestStreamEnd {
List<Employee> list;
@Before
public void before(){
list = EmployeeData.getEmployees();
}
//测试:count 统计Stream中的元素个数
@Test
public void test1() {
//1.获取stream对象
Stream<Employee> stream = list.stream();
//2.终止操作
long count = stream.count();
System.out.println(count);
}
//测试:max/min 获取Stream中的最大值或最小值
@Test
public void test2() {
//1.获取stream对象
Stream<Employee> stream = list.stream();
//2.终止操作
// Optional<Employee> max = stream.max((e1,e2)->Integer.compare(e1.getAge(), e2.getAge()));
Optional<Employee> min = stream.min((e1,e2)->Integer.compare(e1.getAge(), e2.getAge()));
System.out.println(min);
}
//测试:forEach 获取Stream中的内部迭代
@Test
public void test3() {
//1.获取stream对象
Stream<Employee> stream = list.stream();
//2.终止操作
stream.forEach(System.out::println);
}
//测试:reduce 归约,合并
@Test
public void test4() {
//1.获取stream对象
Stream<Employee> stream = list.stream();
//2.终止操作
/*
* BinaryOperator:
* T apply(T t1,T t2);
*/
//进阶1:匿名内部类:
// stream.reduce(new BinaryOperator<Employee>(){
// @Override
// public Employee apply(Employee t, Employee u) {
// return null;
// }
// });
Stream<Double> map = stream.map(e->e.getSalary());
Optional<Double> reduce = map.reduce((t,u)->t+u);
System.out.println(reduce);
}
//测试reduce:归约合并
@Test
public void test4_2() {
Optional<String> reduce = list.stream().map(Employee::getName).reduce((t,u)->t.concat(",").concat(u));
System.out.println(reduce);
}
//案例1:统计工资>100000的个数
@Test
public void exec1() {
long count = list.stream().filter(e->e.getSalary()>100000).count();
System.out.println(count);
}
//练习:员工姓名中包含“黄”的员工个数
@Test
public void exec2() {
//方式一:筛选
long count = list.stream().filter(e->e.getName().contains("黄")).count();
System.out.println(count);
//方式二:map-reduce
Optional<Integer> reduce = list.stream().filter(e->e.getName().contains("黄")).map(e->1).reduce((t,u)->t+u);
System.out.println(reduce);
}
}