简化业务代码开发:看 Lambda 表达式如何将代码封装为数据
摘要:在云服务业务开发中,善于使用代码新特性,往往能让开发效率大大提升,这里简单介绍下lambad表达式及函数式接口特性。
在云服务业务开发中,善于使用代码新特性,往往能让开发效率大大提升,这里简单介绍下lambad表达式及函数式接口特性。
1.Lambda 表达式
Lambda表达式也被称为箭头函数、匿名函数、闭包。他允许把函数作为一个方法的参数(函数作为参数传递到方法中),体现出轻量级函数式编程思想。
为什么引入lambda?
Model Code as Data,编码及数据,尽可能轻量级的将代码封装为数据。
解决方案:接口&实现类(匿名内部类)
存在问题:语法冗余,this关键字、变量捕获、数据控制等
不是解决未知问题的新技术
对现有问题的语义化优化
需要根据实际需求考虑性能问题
2.函数式接口(Functional Interface)
函数式接口就是Java类型系统中的接口,是只包含一个抽象方法的特殊接口(可以有很多非抽象方法)。
语言化检测注解:@FunctionalInterface 检测合法性
java1.8支持接口内包含:抽象方法、默认接口方法、静态接口方法、来自Object继承的方法
JDK 1.8 之前已有的函数式接口:
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
more
JDK 1.8 新增加的函数接口:
java.util.function
3.lambda表达式的基本语法
基本语法
声明:就是和lambda表达式绑定的接口类型
参数:包含在一对圆括号中,和绑定的接口中的抽象方法中的参数个数及顺序一致。
操作符:->
执行代码块:包含在一对大括号中,出现在操作符号的右侧
[接口声明] = (参数) -> {执行代码块};
总结:
lambda表达式,必须和接口进行绑定。
lambda表达式的参数,可以附带0个到n个参数,括号中的参数类型可以不用指定,jvm在运行时,会自动根据绑定的抽象方法中的参数进行推导。
lambda表达式的返回值,如果代码块只有一行,并且没有大括号,不用写return关键字,单行代码的执行结果,会自动返回。 如果添加了大括号,或者有多行代码,必须通过return关键字返回执行结果。
变量捕获
匿名内部类型变量捕获
lambda表达式变量捕获
总结:Lambda表达式优化了匿名内部类类型中的this关键字,不再单独建立对象作用域,表达式本身就是所属类型对象的一部分,在语法语义上使用更加简洁。
类型检查
对于语法相同的表达式,Jvm在运行的过程中,在底层通过解释及重构,进行类型的自动推导。
表达式类型检查
参数类型检查
方法重载
总结:出现方法重载的类型中参数都是函数式接口的情况,需使用匿名内部类实现替代lambda表达式。
底层构建原理
javac Test.java
javap -p Test.class (javap反解析工具 -p显示所有类与成员)
java -Djdk.internal.lambda.dumpProxyClasses Test
声明一个私有静态方法,对Lambda表达式做一个具体的方法实现
声明一个final内部类型并实现接口
在实现接口后的重写方法中利用外部类调用该私有静态方法
4.方法引用
方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
静态方法引用
实例方法引用
构造方法引用
5.Stream
新添加的Stream流—是一个来自数据源的元素队列并支持聚合操作。把真正的函数式编程风格引入到Java中。
不存储数据,也不修改原始源。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
// 1. for循环实现 List list = new ArrayList(); for (String s : list) { if (s.length() > 3) { lista.add(s); } } System.out.println(lista);
几者关系
lambda表达式是传统方法的语法糖,简化并且改造传统内部类实现设计方案的另一种实现模式。
方法引用又是lambda基础上的语法糖,和Stream没有关系,简化方法调用的。
Stream是针对数据和集合的强化优化操作,可以和lambda结合起来简化编码过程。
常见API介绍
1.聚合操作
2.Stream的处理流程
数据源
数据转换[可一到多次转换]
获取结果
3.获取Stream对象
从集合或者数组中获取
Collection.stream(), 如list.stream()
Collection.parallelstream(), 获得支持并发处理的流
Arrays.stream(T t)
BufferReader
BufferReader.lines()-> stream()
静态工厂
java.util.stream.IntStream.range()..
java.nio.file.Files.walk()..
自定构建
java.util.Spliterator
更多的方式
Random.ints()
Pattern.spiltAsStream()..
4.中间操作API{intermediate}:
操作结果是一个Stream对象,所以中间操作可有一个或多个连续的中间操作,需要注意的是中间操作只记录操作方式,不做具体执行,直到结束操作发生时,才做数据的最终执行。
中间操作就是业务逻辑处理
操作过程分为有状态和无状态
无状态:即处理数据时,不受前置中间操作的影响
map/filter/peek/parallel/sequential/unordered
有状态:即处理数据时,受前置中间操作的影响
distant/sorted/limit/skip
5.终结操作|结束操作{Terminal}
一个steam对象只能有一个Terminal操作。这个操作不可逆,一旦发生,就会真实处理数据生成对应结果
非短路操作:当前的Stream对象必须处理完集合中所有的数据,才能得到处理结果
forEach/forEachOrdered/toArray/reduce/collect/min/max/count/iterator
短路操作:当前的Stream对象在处理过程中,一旦满足某个条件,就可以得到结果
anyMatch/AllMatch/noneMatch/findfirst/findAny等
short-circuiting : 在无限大的stream 中返回有限大的stream 需要包含短路操作是有必要的
Stream转换
Stream常见操作
6.案例
问题一:将实例List转化为Map
对于List来说,我需要将其形变为Map<Table.id,Table>,用如下流处理代码
问题二:将集合分成若干类别
使用问题一中的Table类,对于List,我需要将其按照partitionFlag分类,Collector提供两种方法partitioningBy()、groupingBy()。前者分成满足条件与不满足条件两类,后者可按条件分成若干类别的Map。
有的时候,我们关注的不光是元素还有元素的个数,流处理可以再进行后期处理。
Map<Boolean, List<Table>> tablePartition = tableList
.stream().collect(Collectors.partitioningBy(item ->
item.getPartitionFlag() == true,Collectors.counting()));
可输出符合要求的个数。
groupingBy()可对字符串长度分组。
List strings=Arrays.asList(“this”,”is”,”a”,”test”);Map> stringsMap = strings .stream().collect(Collectors.groupingBy(String::length);复制代码
结果输出多分类的map,key值为字符串长度。
注意:如果是从数据库获取数据,务必将分组操作放在数据库中执行,java8新增方法只适合处理内存中的数据。
问题三:从list中得到某个特定的对象
获得List中columnNum最多的table对象
tableList.stream().sorted(comparingInt(Table::getColumnNum)).collect(Collectors.toList()).get(tableList.size()-1);复制代码
添加中间操作reversed() 可获取最小columnNum的对象
问题四: 得到Map<Table,Table.columnNum>中最大columnNum的table
List>list=newArrayList(tableMap.entrySet());Collections.sort(list, (o1, o2) -> (o2.getValue() - o1.getValue()));list.get(0).getKey();
7.性能与安全
串行Stream的性能小于传统的for循环、 迭代器
并行Stream的性能与传统的for循环、 迭代器差不多,在处理对象(复杂数据类型)的情况下,并行性能最佳
本文分享自华为云社区《如何善用函数式接口简化云服务业务代码开发》,原文作者:luanzhen 。