Java8新特性 Lambda、Stream、Optional实现原理
- 一、接口中默认方法修饰为普通方法
- 二、Lambda表达式
-
- 2.1、什么是Lambda表达式
- 2.2、为什么要使用Lambda表达式
- 2.3、Lambda表达式的规范
- 2.4、函数接口定义
- 2.5、Java系统内置那些函数接口
-
- 2.5.1、消费型接口:
- 2.5.2、供给型接口
- 2.5.3、函数型接口
- 2.5.4、断言型接口
- 2.6、Lambda基础语法
-
- 2.6.1、无参方法调用
- 2.6.2、带参数和返回值
- 2.7、方法引入
-
- 2.7.1、什么是方法引入
- 2.7.2、方法引入
-
- 2.7.2.1、方法引入规则
- 2.7.2.2、静态方法引入
- 2.7.2.3、对象方法引入
- 2.7.2.4、实例方法引入
- 2.7.2.5、构造函数引入
- 2.8、Lambda实战案例
-
- 2.8.1、Foreach
- 2.8.2、Lambda集合排序
- 2.8.3、线程调用
- 三、Stream流
-
- 3.1、什么是Stream流
- 3.2、Stream创建方式
- 3.3、Stream将list转换为Set
- 3.4、Stream将list转换为Map
- 3.5、Stream将Reduce 求和
- 3.6、StreamMax和Min
- 3.7、StreamMatch 匹配
- 3.8、StreamFor循环
- 3.9、Stream过滤器
- 3.10、Stream排序 sorted
- 3.11、Stream limit和skip
- 3.12、并行流与串行流区别
- 四、JDK8 Optional
-
- 4.1、判断参数是否为空
- 4.2、参数为空可以设定默认值
- 4.3、参数实现过滤
- 4.4、与Lambda表达式结合使用,优化代码
-
- 4.4.1、优化方案1
- 4.4.2、优化方案2
- 4.4.3、优化方案3
一、接口中默认方法修饰为普通方法
在jdk8之前,interface之中可以定义变量和方法,变量必须是public、static、final的,方法必须是public、abstract的,由于这些修饰符都是默认的。
接口定义方法:public 抽象方法 需要子类实现
接口定义变量:public、static、final
在JDK 1.8开始 支持使用static和default 修饰 可以写方法体,不需要子类重写。
方法:
- 普通方法 可以有方法体
- 抽象方法 没有方法体需要子类实现 重写。
二、Lambda表达式
2.1、什么是Lambda表达式
Lambda 好处:
- 简化我们匿名内部类的调用。
- Lambda + 方法引入 代码变得更加精简。
- Lambda 表达式(lambda expression)是一个匿名函数,简化我们调用匿名函数的过程。
2.2、为什么要使用Lambda表达式
可以非常简洁的形式调用我们的匿名函数接口。
OrderService
public interface OrderService {
void addOrder();
}
OrderServiceImpl
public class OrderServiceImpl implements OrderService {
@Overridepublic void addOrder() {
System.out.println("添加订单");}
}
Test01
public static void main(String[] args) {
//1.使用new的实现类的形式调用接口OrderService orderService = new OrderServiceImpl();orderService.addOrder();//2.使用匿名内部接口调用new OrderService() {
@Overridepublic void addOrder() {
System.out.println("使用匿名内部类的形式调用接口");}}.addOrder();// 3.使用lambda调用接口OrderService orderService2 = () -> System.out.println("使用lambda调用接口");orderService2.addOrder();}
2.3、Lambda表达式的规范
使用Lambda表达式 依赖于函数接口
- 在接口中只能够允许有一个抽象方法
- 在函数接口中定义object类中方法
- 使用默认或者静态方法
- @FunctionalInterface 表示该接口为函数接口
Java中使用Lambda表达式的规范,必须是为函数接口
函数接口的定义:
- 在该接口中只能存在一个抽象方法,该接口称作为函数接口
JDK中自带的函数接口:
- java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
- java.util.Comparator
- java.io.FileFilter
- java.nio.file.PathMatcher
- java.lang.reflect.InvocationHandler
- java.beans.PropertyChangeListener
- java.awt.event.ActionListener
- javax.swing.event.ChangeListener
我们也可以使用@FunctionalInterface修饰为函数接口
2.4、函数接口定义
- 在接口中只能有一个抽象方法
- @FunctionalInterface 标记为该接口为函数接口
- 可以通过default 修饰为普通方法
- 可以定义object类中的方法
@FunctionalInterface
public interface MyFunctionalInterface {
void add();default void get(){
}String toString();
}
2.5、Java系统内置那些函数接口
2.5.1、消费型接口:
- Conusmer
void accept(T t); - BiConusmer<T,U>
void accept(T t,U u);//增加一种入参类型
2.5.2、供给型接口
- Supplier
void get();
2.5.3、函数型接口
- Function<T ,R>
R apply(T t); - UnaryOperator
T apply(T t);//入参与返回值类型一致 - BiFunction <T ,U,R>
R apply(T t,U u);//增加一个参数类型 - BinaryOperator
T apply(T t1,T t2);//l两个相同类型入参与同类型返回值 - ToIntFunction//限定返回int
- ToLongFunction//限定返回long
- ToDoubleFunction//限定返回double
- IntFunction//限定入参int,返回泛型R
- LongFunction//限定入参long,返回泛型R
- DoubleFunction//限定入参double,返回泛型R
2.5.4、断言型接口
- Predicate
- boolean test(T t)
2.6、Lambda基础语法
语法格式一: 无参数,无返回值。 () -> { }
Runnable runnable = () -> System.out.print("Hello Lambda");
语法格式二: 有一个参数,并且无返回值。 (x) ->{ }
Consumer<String> consumer = (x) -> System.out.print("Hello Lambda");
语法格式三: 有两个以上参数,有返回值, 并且Lambda式中有多条语句。
Comparator<Integer> comparator = (x, y) -> {
System.out.print("Hello Lambda");return Integer.compare(x,y);
};
若Lambda体中只有一条语句,return 和 大括号都可以省略不写。
Lambda表达式参数列表的数据类型可以省略不写,因为JVM的编译器通过上下文可以推断出类型,即 “类型推断”
Lambda表达式需要“函数式接口”的支持。
函数式接口: 接口中只有一个抽象方法的接口,称为函数式接口。可以使用注解@FunctionalInterface修饰 来检查是否是函数式接口。
2.6.1、无参方法调用
AcanthopanaxInterface
public interface AcanthopanaxInterface {
void get();
}
Test
public class Test06 {
public static void main(String[] args) {
AcanthopanaxInterface acanthopanaxInterface = () -> System.out.println("使用lamdba表达式调用方法");acanthopanaxInterface.get();}
}
2.6.2、带参数和返回值
YouCanInterface
public interface YouCanInterface {
String get(int i, int j);
}
Test07
public class Test07 {
public static void main(String[] args) {
YouCanInterface youCanInterface = new YouCanInterface() {
@Overridepublic String get(int i, int j) {
return i + "----" + j;}};System.out.println(youCanInterface.get(2, 3));}
}
精简语法
public class Test07 {
public static void main(String[] args) {
YouCanInterface youCanInterface = (i, j) -> i + "----" + j;System.out.println(youCanInterface.get(2, 3));}
}
2.7、方法引入
2.7.1、什么是方法引入
需要结合lambda表达式能够让代码变得更加精简。
- 匿名内部类使用
- Lambda调用匿名内部类
- 方法引入
2.7.2、方法引入
- 静态方法引入: 类名::(静态)方法名称
- 对象方法引入 类名:: 实例方法名称
- 实例方法引入 new对象 对象实例::方法引入
- 构造函数引入 类名::new
需要遵循一个规范:
方法引入 方法参数列表、返回类型与函数接口参数列表与返回类型必须
要保持一致。
Lambda: 匿名内部类使用代码简洁问题
类型 | 语法 | 对应lambda表达式 |
---|---|---|
构造器引用 | Class::new | (args) -> new 类名(args) |
静态方法引用 | Class::static_method | (args) -> 类名.static_method(args) |
对象方法引用 | Class::method (inst,args) | (inst,args) -> 类名.method(args) |
实例方法引用 | instance::method (args) | (args) -> instance.method(args) |
-
方法引用提供了非常有用的语法,可以直接引用已有的java类或对象的方法或构造器。方法引用其实也离不开Lambda表达式,
-
与lambda联合使用 ,方法引用可以使语言的构造更加紧凑简洁,减少冗余代码。
-
方法引用提供非常有用的语法,可以直接引用已有的java类或者对象中方法或者构造函数,
-
方法引用需要配合Lambda表达式语法一起使用减少代码的冗余性问题。
2.7.2.1、方法引入规则
方法引入实际上就是lambda表达式中直接引入的方法。
必须遵循规范:引入的方法参数列表返回类型必须要和函数接口参数列表、返回
类型保持一致。
2.7.2.2、静态方法引入
MessageInterface
@FunctionalInterface
public interface MessageInterface {
void get();
}
MethodReference
public class MethodReference {
public static void main(String[] args) {
//1.使用匿名内部类的形式 调用get方法new MessageInterface() {
@Overridepublic void get() {
MethodReference.getMethod();}}.get();//2.使用lambda((MessageInterface) () -> MethodReference.getMethod()).get();// 使用方法引入调用方法 必须满足:方法引入的方法必须和函数接口中的方法参数列表/返回值一定保持一致。MessageInterface messageInterface = MethodReference::getMethod;messageInterface.get();}public static void getMethod() {
System.out.println("我是getMethod");}
}
2.7.2.3、对象方法引入
ObjectMethodService
public interface ObjectMethodService {
String get(Test08 test08);
}
Test08
public class Test08 {
public static void main(String[] args) {
// 1.使用匿名内部类的形式ObjectMethodService objectMethodService = new ObjectMethodService() {
@Overridepublic String get(Test08 test08) {
return test08.objGet();}};System.out.println(objectMethodService.get(new Test08()));// 2.lambdaObjectMethodService objectMethodService1 = test08 -> test08.objGet();System.out.println(objectMethodService1.get(new Test08()));// 3.方法引入 在这时候我们函数接口 第一个参数传递test23 返回调用test23.objGet方法//Test08::objGet; == (test08) -> test08.objGet();ObjectMethodService objectMethodService2 = Test08::objGet;System.out.println(objectMethodService2.get(new Test08()));// 需要将string类型字符串获取长度Function<String, Integer> function2 = String::length;System.out.println(function2.apply("hello world"));}public String objGet() {
return "hello world";}
}
2.7.2.4、实例方法引入
MessageInterface2
@FunctionalInterface
public interface MessageInterface2 {
void get(Integer a);
}
Test09
public class Test09 {
public static void main(String[] args) {
//1.匿名内部类的写法Test09 test09 = new Test09();MessageInterface2 messageInterface = new MessageInterface2() {
@Overridepublic void get(Integer a) {
test09.get(a);}};messageInterface.get(6);//lambdaMessageInterface2 messageInterface2 = a -> test09.get(a);}public void get(Integer a) {
System.out.println("方法引入get方法:" + a);}
}
2.7.2.5、构造函数引入
User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;private Integer age;
}
UserInterface
public interface UserInterface {
User getUser();
}
Test10
public class Test10 {
public static void main(String[] args) {
//1.匿名内部类UserInterface userInterface = new UserInterface() {
@Overridepublic User getUser() {
return new User();}};System.out.println(userInterface.getUser());//lambdaUserInterface userInterface2 = () -> new User();System.out.println(userInterface2.getUser());//构造函数UserInterface userInterface3 = User::new;System.out.println(userInterface3.getUser());}
}
2.8、Lambda实战案例
2.8.1、Foreach
public class Test03 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();list.add("java");list.add("php");list.add("python");list.forEach(new Consumer<String>() {
@Overridepublic void accept(String s) {
System.out.println("s = " + s);}});System.out.println("=================");list.forEach((String s)-> System.out.println("s = " + s));}
}
2.8.2、Lambda集合排序
User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;private Integer age;
}
Test04
public class Test04 {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();userList.add(new User("zhangsan", 26));userList.add(new User("lisi", 18));userList.add(new User("wagnwu", 23));userList.sort(new Comparator<User>() {
@Overridepublic int compare(User o1, User o2) {
return o1.getAge() - o2.getAge();}});System.out.println("userList = " + userList);System.out.println("=========");userList.sort((User o1, User o2) -> o2.getAge() - o1.getAge());System.out.println("userList = " + userList);System.out.println("=========");}
}
2.8.3、线程调用
public class Test05 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Overridepublic void run() {
System.out.println(Thread.currentThread().getName() + ",线程执行了");}}).start();new Thread(() -> System.out.println(Thread.currentThread().getName() + ",线程执行了")).start();}
}
三、Stream流
3.1、什么是Stream流
Stream 是JDK1.8 中处理集合的关键抽象概念,Lambda 和 Stream 是JDK1.8新增的函数式编程最有亮点的特性了,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用SQL执行的数据库查询。Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
Stream :非常方便精简的形式遍历集合实现 过滤、排序等。
3.2、Stream创建方式
parallelStream为并行流采用多线程执行
Stream采用单线程执行
parallelStream效率比Stream要高。
public class Test11 {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();userList.add(new User("东来佛组", 200000));userList.add(new User("如来佛组", 100000));userList.add(new User("未来佛组", 50000));userList.add(new User("斗战圣佛", 600));Stream<User> stream = userList.stream();Stream<User> userStream = userList.parallelStream();}
}
3.3、Stream将list转换为Set
Test11
public class Test11 {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();userList.add(new User("东来佛组", 200000));userList.add(new User("如来佛组", 100000));userList.add(new User("未来佛组", 50000));userList.add(new User("斗战圣佛", 600));Stream<User> stream = userList.stream();Set<User> set = stream.collect(Collectors.toSet());System.out.println("set = " + set);}
}
3.4、Stream将list转换为Map
public class Test12 {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();userList.add(new User("东来佛组", 200000));userList.add(new User("如来佛组", 100000));userList.add(new User("未来佛组", 50000));userList.add(new User("斗战圣佛", 600));Stream<User> stream = userList.stream();Map<String, User> collect = stream.collect(Collectors.toMap(new Function<User, String>() {
@Overridepublic String apply(User user) {
return user.getName();}}, new Function<User, User>() {
@Overridepublic User apply(User user) {
return user;}}));collect.forEach(new BiConsumer<String, User>() {
@Overridepublic void accept(String key, User user) {
System.out.println("key = " + key + ", value = " + user);}});}
}
3.5、Stream将Reduce 求和
public class Test13 {
public static void main(String[] args) {
Stream<Integer> integerStream = Stream.of(10, 20, 50, 45, 48, 56, 19);
// Optional<Integer> reduce = integerStream.reduce(new BinaryOperator<Integer>() {
// @Override
// public Integer apply(Integer integer, Integer integer2) {
// return integer + integer2;
// }
// });Optional<Integer> reduce = integerStream.reduce((integer, integer2) -> integer + integer2);System.out.println(reduce.get());List<User> userList = new ArrayList<>();userList.add(new User("东来佛组", 200000));userList.add(new User("如来佛组", 100000));userList.add(new User("未来佛组", 50000));userList.add(new User("斗战圣佛", 600));Stream<User> stream = userList.stream();
// Optional<User> reduce1 = stream.reduce(new BinaryOperator<User>() {
// @Override
// public User apply(User user, User user2) {
// user.setAge(user.getAge() + user2.getAge());
// return user;
// }
// });Optional<User> reduce1 = stream.reduce((user, user2) -> {
user.setAge(user.getAge() + user2.getAge());return user;});System.out.println("reduce1 = " + reduce1);}
}
3.6、StreamMax和Min
public class Test14 {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();userList.add(new User("东来佛组", 200000));userList.add(new User("如来佛组", 100000));userList.add(new User("未来佛组", 50000));userList.add(new User("斗战圣佛", 600));Stream<User> stream = userList.stream();
// Optional<User> maxUser = stream.max(new Comparator<User>() {
// @Override
// public int compare(User o1, User o2) {
// return o1.getAge() - o2.getAge();
// }
// });Optional<User> maxUser = stream.max((o1, o2) -> o1.getAge() - o2.getAge());System.out.println(maxUser.get());//流被关闭 重新打开stream = userList.stream();Optional<User> minUser = stream.min((o1, o2) -> o1.getAge() - o2.getAge());System.out.println(minUser.get());}
}
3.7、StreamMatch 匹配
anyMatch表示,判断的条件里,任意一个元素成功,返回true
allMatch表示,判断条件里的元素,所有的都是,返回true
noneMatch跟allMatch相反,判断条件里的元素,所有的都不是,返回true
public class Test15 {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();userList.add(new User("东来佛组", 200000));userList.add(new User("如来佛组", 100000));userList.add(new User("未来佛组", 50000));userList.add(new User("斗战圣佛", 600));Stream<User> stream = userList.stream();
// boolean result = stream.noneMatch(new Predicate<User>() {
// @Override
// public boolean test(User user) {
// return user.getAge() > 99999;
// }
// });boolean result = stream.noneMatch(user -> user.getAge() > 99999);System.out.println("result = " + result);}
}
3.8、StreamFor循环
public class Test16 {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();userList.add(new User("东来佛组", 200000));userList.add(new User("如来佛组", 100000));userList.add(new User("未来佛组", 50000));userList.add(new User("斗战圣佛", 600));Stream<User> stream = userList.stream();// stream.forEach(new Consumer<User>() {
// @Override
// public void accept(User user) {
// System.out.println("user = " + user);
// }
// });stream.forEach(user -> System.out.println("user = " + user));}
}
3.9、Stream过滤器
public class Test17 {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();userList.add(new User("东来佛组", 200000));userList.add(new User("如来佛组", 100000));userList.add(new User("未来佛组", 50000));userList.add(new User("斗战圣佛", 600));userList.add(new User("斗战圣佛", 1000));Stream<User> stream = userList.stream();// stream.filter(new Predicate<User>() {
// @Override
// public boolean test(User user) {
// return user.getAge() < 10000;
// }
// }).filter(new Predicate<User>() {
// @Override
// public boolean test(User user) {
// return user.getName().equals("斗战圣佛");
// }
// }).forEach(new Consumer<User>() {
// @Override
// public void accept(User user) {
// System.out.println("user = " + user);
// }
// });stream.filter(user -> user.getAge() < 10000).filter(user -> user.getName().equals("斗战圣佛")).forEach(user -> System.out.println("user = " + user));}
}
3.10、Stream排序 sorted
public class Test18 {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();userList.add(new User("东来佛组", 200000));userList.add(new User("如来佛组", 100000));userList.add(new User("未来佛组", 50000));userList.add(new User("斗战圣佛", 600));Stream<User> stream = userList.stream();
// stream.sorted(new Comparator<User>() {
// @Override
// public int compare(User o1, User o2) {
// return o1.getAge() - o2.getAge();
// }
// }).forEach(new Consumer<User>() {
// @Override
// public void accept(User user) {
// System.out.println("user = " + user);
// }
// });stream.sorted((o1, o2) -> o1.getAge() - o2.getAge()).forEach(user -> System.out.println("user = " + user));}
}
3.11、Stream limit和skip
Limit 从头开始获取
Skip 就是跳过
public class Test19 {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();userList.add(new User("东来佛组", 200000));userList.add(new User("如来佛组", 100000));userList.add(new User("未来佛组", 50000));userList.add(new User("斗战圣佛", 600));Stream<User> stream = userList.stream();stream.skip(2).limit(1).forEach(user -> System.out.println("user = " + user));}
}
3.12、并行流与串行流区别
串行流:
- 单线程的方式操作; 数据量比较少的时候。
并行流:
- 多线程方式操作;数据量比较大的时候,原理:
Fork join 将一个大的任务拆分n多个小的子任务并行执行,
最后在统计结果,有可能会非常消耗cpu的资源,确实可以
提高效率。
注意:数据量比较少的情况下,不要使用并行流。
四、JDK8 Optional
- Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
- Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
- Optional 类的引入很好的解决空指针异常。
4.1、判断参数是否为空
ofNullable(可以传递一个空对象)
of(不可以传递空对象,传null会报NullPointerException)
Integer a = 1;
Optional<Integer> result = Optional.ofNullable(a);
System.out.println(result.isPresent());
- isPresent true 不为空
- isPresent返回 false 为空。
4.2、参数为空可以设定默认值
Integer a = 1;
Optional<Integer> result = Optional.ofNullable(a);
System.out.println(result.get());
System.out.println(result.isPresent());
a = null;
Integer rs = Optional.ofNullable(a).orElse(2);
System.out.println(rs);
4.3、参数实现过滤
Integer val = 16;
Optional<Integer> result = Optional.ofNullable(val);
boolean isPresent = result.filter(a2 -> a2 > 17).isPresent();
System.out.println(isPresent);
4.4、与Lambda表达式结合使用,优化代码
4.4.1、优化方案1
// 优化前
String name = "hello";
if (name != null) {
System.out.println(name);
}
//优化后
Optional<String> name2 = Optional.ofNullable(name);
// 当value 不为空时,则不会调用
//name2.ifPresent(s -> System.out.println(s));
name2.ifPresent(System.out::print);
4.4.2、优化方案2
public class Test26 {
private static User user = null;public static void main(String[] args) {
User user = Test26.getUser();System.out.println(user);}public static User getUser() {
// 优化前
// if (user == null){
// return createUser();
// }
// return user;// 优化后
// return Optional.ofNullable(user).orElseGet(new Supplier<User>() {
// @Override
// public User get() {
// return createUser();
// }
// });return Optional.ofNullable(user).orElseGet(() -> createUser());}private static User createUser() {
return new User("java", 25);}
}
4.4.3、优化方案3
map中获取的返回值自动被Optional包装,即返回值 -> Optional<返回值>
flatMap中返回值保持不变,但必须是Optional类型,即Optional<返回值> -> Optional<返回值>
public class Test27 {
public static void main(String[] args) {
String name = Test27.getName();System.out.println(name);}public static String getName() {
User user = new User("Java521", 66);// 优化前写法:
// if (user != null) {
// String name = user.getName();
// if (name != null) {
// return name.toLowerCase();
// }
// }
// return null;//优化后写法:return Optional.ofNullable(user).map(user1 -> {
return user1.getName();}).map(name -> {
return name.toLowerCase();}).orElse(null);}
}