代码改变世界大数据

简化业务代码开发:看 Lambda 表达式如何将代码封装为数据

2021-01-18  本文已影响0人  华为云开发者联盟

摘要:在云服务业务开发中,善于使用代码新特性,往往能让开发效率大大提升,这里简单介绍下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 。

上一篇下一篇

猜你喜欢

热点阅读