首先盗一张图,这张图上面列出了java8所体现的全部优点:
本次梳理的知识点大概有默认方法、函数式接口、Lambda表达式和方法引用、Stream API
一.接口默认方法
1.什么是接口默认方法?
顾名思义:默认方法就是接口的默认的方法。
2.用法
@FunctionalInterface
public interface MyInterface {
Integer handler(Integer t);
default Integer handler2(Integer t) {
return null;
}
default Integer handler3(Integer t) {
return null;
}
}
3.优势
1).默认方法的主要优势是提供一种拓展接口的方法,而不破坏现有代码。加入我们有一个已经投入使用接口需要拓展一个新的方法,在JDK8以前,如果为一个使用的接口增加一个新方法,则我们必须在所有实现类中添加该方法的实现,否则编译会出现异常。如果实现类数量少并且我们有权限修改,可能会工作量相对较少。如果实现类比较多或者我们没有权限修改实现类源代码,这样可能就比较麻烦。而默认方法则解决了这个问题,它提供了一个实现,当没有显示提供其他实现时就采用这个实现。这样新添加的方法将不会破坏现有代码。
2).默认方法的另一个优势是该方法是可选的,子类可以根据不同的需求Override默认实现。例如,我们定义一个集合接口,其中有增、删、改等操作。如果我们的实现类90%都是以数组保存数据,那么我们可以定义针对这些方法给出默认实现,而对于其他非数组集合或者有其他类似业务,可以选择性复写接口中默认方法。
4.注意
实现类里面调用接口的默认方法不能访问实现类的成员变量,这个是需要注意的。
二.函数式接口
1.什么是函数式接口?
所谓的函数式接口就是只有一个抽象方法的接口。
2.栗子:
普通的接口
public interface MyInterface {
Integer handler(Integer t);
Integer handler1(Integer t);
default Integer handler2(Integer t) {
return null;
}
default Integer handler3(Integer t) {
return null;
}
}
函数式接口
@FunctionalInterface
public interface MyInterface {
Integer handler(Integer t);
default Integer handler2(Integer t) {
return null;
}
default Integer handler3(Integer t) {
return null;
}
}
3.作用:
为Lambda表达式服务,是Lambda表达式各种实现的抽象方法。
三.Lambda表达式和方法引用
有了函数式接口之后,就可以使用Lambda表达式和方法引用了。其实函数式接口的表中的函数描述符就是Lambda表达式,在函数式接口中Lambda表达式相当于匿名内部类的效果.
1.栗子:
@Test
public void test(){
//Jdk8之前
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("t thread start...");
}
});
t.start();
//Jdk8之后
Thread t1 = new Thread(() -> System.out.println("t1 thread start..."));
t1.start();
}
2.语法:
3.使用:
List emps = Arrays.asList(
new Employee(101, "张三", 18, 9999.99),
new Employee(102, "李四", 59, 6666.66),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
//需求:获取公司中年龄小于 35 的员工信息
public List filterEmployeeAge(List emps){
List list = new ArrayList<>();
for (Employee emp : emps) {
if(emp.getAge() <= 35){
list.add(emp);
}
}
return list;
}
@Test
public void test3(){
List list = filterEmployeeAge(emps);
for (Employee employee : list) {
System.out.println(employee);
}
}
//需求:获取公司中工资大于 5000 的员工信息
public List filterEmployeeSalary(List emps){
List list = new ArrayList<>();
for (Employee emp : emps) {
if(emp.getSalary() >= 5000){
list.add(emp);
}
}
return list;
}
@Test
public void test4(){
List list = filterEmployeeSalary(emps);
for (Employee employee : list) {
System.out.println(employee);
}
}
//优化方式一:策略设计模式
public List filterEmployee(List emps, MyPredicate mp){
List list = new ArrayList<>();
for (Employee employee : emps) {
if(mp.test(employee)){
list.add(employee);
}
}
return list;
}
@Test
public void test5(){
List list = filterEmployee(emps, new FilterEmployeeForAge());
for (Employee employee : list) {
System.out.println(employee);
}
System.out.println("------------------------------------------");
List list2 = filterEmployee(emps, new FilterEmployeeForSalary());
for (Employee employee : list2) {
System.out.println(employee);
}
}
//优化方式二:匿名内部类
@Test
public void test6(){
List list = filterEmployee(emps, new MyPredicate() {
@Override
public boolean test(Employee t) {
return t.getId() <= 103;
}
});
for (Employee employee : list) {
System.out.println(employee);
}
}
//优化方式三:Lambda 表达式
@Test
public void test7(){
List list = filterEmployee(emps, (e) -> e.getAge() <= 35);
list.forEach(System.out::println);
System.out.println("------------------------------------------");
List list2 = filterEmployee(emps, (e) -> e.getSalary() >= 5000);
list2.forEach(System.out::println);
}
//优化方式四:Stream API
@Test
public void test8(){
emps.stream()
.filter((e) -> e.getAge() <= 35)
.forEach(System.out::println);
System.out.println("----------------------------------------------");
emps.stream()
.map(Employee::getName)
.limit(3)
.sorted()
.forEach(System.out::println);
}
4.四大内置函数式接口的支持
java.util.function
/* * Java8 内置的四大核心函数式接口 * * Consumer: 消费型接口 * void accept(T t); * * Supplier : 供给型接口 * T get(); * * Function : 函数型接口 * R apply(T t); * * Predicate : 断言型接口 * boolean test(T t); * */
5.方法引用
我们要表达的lambda表达式已经有方法实现了,我们就直接调用,调用的方式就叫做方法引用,方法引用也是一种lambda的表现形式。
方法引用的标准形式是:类名::方法名
。(注意:只需要写方法名,不需要写括号)
有以下四种形式的方法引用:
类型示例
引用静态方法 | ContainingClass::staticMethodName |
引用某个对象的实例方法 | containingObject::instanceMethodName |
引用某个类型的任意对象的实例方法 | ContainingType::methodName |
引用构造方法 | ClassName::new |
引用对象实例方法
//对象的引用 :: 实例方法名
@Test
public void test2(){
Employee emp = new Employee(101, "张三", 18, 9999.99);
Supplier sup = () -> emp.getName();
System.out.println(sup.get());
System.out.println("----------------------------------");
Supplier sup2 = emp::getName;
System.out.println(sup2.get());
}
引用的方法的参数列表和返回值要和替换的lambda表达式的参数列表和返回值保持一直。
引用类的实例方法
//类名 :: 实例方法名
@Test
public void test5(){
BiPredicate bp = (x, y) -> x.equals(y);
System.out.println(bp.test("abcde", "abcde"));
System.out.println("-----------------------------------------");
BiPredicate bp2 = String::equals;
System.out.println(bp2.test("abc", "abc"));
System.out.println("-----------------------------------------");
Function fun = (e) -> e.show();
System.out.println(fun.apply(new Employee()));
System.out.println("-----------------------------------------");
Function fun2 = Employee::show;
System.out.println(fun2.apply(new Employee()));
}
lambda表达式的第一个参数是实例方法的调用者,第二个参数是实例方法的参数时,才可以使用 ClassName::method
四.Stream
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
1.与集合的关系
可以简单理解为集合是描述的数据,stream表示的如何操作数据的计算方式。 slogen:不生产数据,只是数据的搬运工~
2.使用(把大象装冰箱分三步~)
-
1.创建Stream
一个数据源(如:集合、数组),获取一个流 -
2.中间操作
一个中间操作链,对数据源的数据进行处理 -
3.终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果
3.创建
@Test
public void test1(){
//1.可以通过Collection 系列集合提供的stream()或parallelStream()
List list = new ArrayList<>();
Stream stream1 = list.stream();
//2.通过 Arrays 中的静态方法stream()获取数组流
Employee[] emps=new Employee[10];
Stream stream2=Arrays.stream(emps);
//3.通过Stream 类中的静态方法of()
Stream stream3=Stream.of("aa","bb","cc");
//4.创建无限流
//迭代
Stream stream4=Stream.iterate(0, (x) -> x+2);
stream4.limit(10).forEach(System.out::println);
//生成
Stream.generate(() -> Math.random())
.limit(5)
.forEach(System.out::println);
}
4.中间操作
4.1.筛选与切片
//中间操作
List employees=Arrays.asList(
new Employee("张三",18,9999.99),
new Employee("李四",58,5555.55),
new Employee("王五",26,3333.33),
new Employee("赵六",36,6666.66),
new Employee("田七",12,8888.88),
new Employee("田七",12,8888.88)
);
/* 筛选与切片
* filter--接收Lambda,从流中排除某些元素。
* limit--截断流,使其元素不超过给定数量。
* skip(n)--跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n) 互补
* distinct--筛选,通过流所生成元素的 hashCode() 和 equals() 去掉重复元素
*/
//内部迭代:迭代操作由 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(){//发现“短路”只输出了两次,说明只要找到 2 个 符合条件的就不再继续迭代
employees.stream()
.filter((e)->{
System.out.println("短路!");
return e.getSalary()>5000;
})
.limit(2)
.forEach(System.out::println);
}
@Test
public void test4(){
employees.stream()
.filter((e)->e.getSalary()>5000)
.skip(2)//跳过前两个
.distinct()//去重,注意:需要Employee重写hashCode 和 equals 方法
.forEach(System.out::println);
}
4.2.映射
//中间操作
/*
* 映射
* map--接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新元素。
* flatMap--接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
*/
@Test
public void test5(){
List list=Arrays.asList("aaa","bbb","ccc","ddd");
list.stream()
.map((str)->str.toUpperCase())
.forEach(System.out::println);
System.out.println("------------------------");
employees.stream()
.map(Employee::getName)
.forEach(System.out::println);
System.out.println("------------------------");
Stream> stream=list.stream()
.map(TestStreamAPI2::filterChatacter);
stream.forEach((sm)->{
sm.forEach(System.out::println);
});
System.out.println("------------------------");
Stream sm=list.stream()
.flatMap(TestStreamAPI2::filterChatacter);
sm.forEach(System.out::println);
}
public static Stream filterChatacter(String str){
List list=new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
@Test
public void test6(){//map和flatMap的关系 类似于 add(Object)和addAll(Collection coll)
List list=Arrays.asList("aaa","bbb","ccc","ddd");
List list2=new ArrayList<>();
list2.add(11);
list2.add(22);
list2.addAll(list);
System.out.println(list2);
}
4.3.排序
//中间操作
/*
* 排序
* sorted()-自然排序(按照对象类实现Comparable接口的compareTo()方法 排序)
* sorted(Comparator com)-定制排序(Comparator)
*/
@Test
public void test7(){
List list=Arrays.asList("ccc","bbb","aaa");
list.stream()
.sorted()
.forEach(System.out::println);
System.out.println("------------------------");
employees.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);
}
5.终止操作
5.1查找与匹配
List employees=Arrays.asList(
new Employee("张三",18,9999.99,Status.FREE),
new Employee("李四",58,5555.55,Status.BUSY),
new Employee("王五",26,3333.33,Status.VOCATION),
new Employee("赵六",36,6666.66,Status.FREE),
new Employee("田七",12,8888.88,Status.BUSY)
);
/*
* 查找与匹配
*
*/
@Test
public void test1(){
boolean b1=employees.stream()//allMatch-检查是否匹配所有元素
.allMatch((e)->e.getStatus().equals(Status.BUSY));
System.out.println(b1);//false
boolean b2=employees.stream()//anyMatch-检查是否至少匹配一个元素
.anyMatch((e)->e.getStatus().equals(Status.BUSY));
System.out.println(b2);//true
boolean b3=employees.stream()//noneMatch-检查是否没有匹配所有元素
.noneMatch((e)->e.getStatus().equals(Status.BUSY));
System.out.println(b3);//false
Optional op=employees.stream()//findFirst-返回第一个元素//Optional是Java8中避免空指针异常的容器类
.sorted((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()))
.findFirst();
System.out.println(op.get());//Employee [name=王五, age=26, salary=3333.33, Status=VOCATION]
Optional op2=employees.parallelStream()//findAny-返回当前流中的任意元素
.filter((e)->e.getStatus().equals(Status.FREE))
.findAny();
System.out.println(op2.get());//Employee [name=赵六, age=36, salary=6666.66, Status=FREE]
Long count=employees.stream()//count-返回流中元素的总个数
.count();
System.out.println(count);//5
Optional op3=employees.stream()//max-返回流中最大值
.max((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(op3.get());//Employee [name=张三, age=18, salary=9999.99, Status=FREE]
Optional op4=employees.stream()//min-返回流中最小值
.map(Employee::getSalary)
.min(Double::compare);
System.out.println(op4.get());//3333.33
}
5.2归约
/*
* 归约
* reduce(T identity,BinaryOperator b) / reduce(BinaryOperator b)-可以将流中元素反复结合起来,得到一个值。
*/
@Test
public void test3(){
List list=Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum=list.stream()//reduce(T identity,BinaryOperator b)
.reduce(0, (x,y)->x+y);//0为起始值
System.out.println(sum);
System.out.println("--------------------------");
Optional op=employees.stream()//reduce(BinaryOperator b)//没有起始值,map返回可能为空,所以返回Optional类型
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(op.get());
}
5.3收集
/*
* 收集
* collect-将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。
*/
@Test
public void test4(){
List list=employees.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println("----------------------------");
Set set=employees.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
System.out.println("----------------------------");
HashSet hs=employees.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
hs.forEach(System.out::println);
System.out.println("----------------------------");
//总和
Long count=employees.stream()
.collect(Collectors.counting());
System.out.println(count);
//平均值
Double avg=employees.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
//总和
Double sum=employees.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
//最大值
Optional max=employees.stream()
.collect(Collectors.maxBy((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(max.get());
//最小值
Optional min=employees.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compare));
System.out.println(min.get());
System.out.println("----------------------------");
//分组
Map> map=employees.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(map);//{FREE=[Employee [name=张三, age=18, salary=9999.99, Status=FREE], Employee [name=赵六, age=36, salary=6666.66, Status=FREE]], VOCATION=[Employee [name=王五, age=26, salary=3333.33, Status=VOCATION]], BUSY=[Employee [name=李四, age=58, salary=5555.55, Status=BUSY], Employee [name=田七, age=12, salary=8888.88, Status=BUSY]]}
//多级分组
Map>> map2=employees.stream()
.collect( Collectors.groupingBy( Employee::getStatus,Collectors.groupingBy((e)->{
if(e.getAge()<=35){
return "青年";
}else if(e.getAge()<=50){
return "中年";
}else{
return "老年";
}
}) ) );
System.out.println(map2);//{FREE={青年=[Employee [name=张三, age=18, salary=9999.99, Status=FREE]], 中年=[Employee [name=赵六, age=36, salary=6666.66, Status=FREE]]}, VOCATION={青年=[Employee [name=王五, age=26, salary=3333.33, Status=VOCATION]]}, BUSY={青年=[Employee [name=田七, age=12, salary=8888.88, Status=BUSY]], 老年=[Employee [name=李四, age=58, salary=5555.55, Status=BUSY]]}}
//分区
Map> map3=employees.stream()
.collect(Collectors.partitioningBy((e)->e.getSalary()>8000));
System.out.println(map3);//{false=[Employee [name=李四, age=58, salary=5555.55, Status=BUSY], Employee [name=王五, age=26, salary=3333.33, Status=VOCATION], Employee [name=赵六, age=36, salary=6666.66, Status=FREE]], true=[Employee [name=张三, age=18, salary=9999.99, Status=FREE], Employee [name=田七, age=12, salary=8888.88, Status=BUSY]]}
System.out.println("--------------------------------");
DoubleSummaryStatistics dss=employees.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getSum());
System.out.println(dss.getAverage());
System.out.println(dss.getMax());
System.out.println("--------------------------------");
String strr=employees.stream()
.map(Employee::getName)
.collect(Collectors.joining(","));
System.out.println(strr);//张三李四王五赵六田七
}
备注: