文档章节

Java8新特性:函数式编程

BakerZhu
 BakerZhu
发布于 2018/08/05 22:15
字数 2073
阅读 205
收藏 0

3 月,跳不动了?>>>

GitHub

Lambda表达式

Lambda表达式形式

Java 中表达式一共有五中基本形式 , 具体如下:

public class LambdaExpression {
    
    public static void express() {

        // 1----------------------------------------------
        Runnable runnable = () -> System.out.println("Hello World");
        // 2----------------------------------------------
        ActionListener oneArgument = event -> System.out.println("button clicked");
        // 3----------------------------------------------
        Runnable multiStatement = () -> {
            System.out.print("Hello");
            System.out.println(" World");
        };
        // 4----------------------------------------------
        BinaryOperator<Long> add = (x , y) -> x + y;

        new BinaryOperator<Long>() {
            @Override
            public Long apply(Long aLong, Long aLong2) {
                return null;
            }
        };
        // 5----------------------------------------------
        BinaryOperator<Long> addOper = (Long x , Long y) -> x + y;
    }
}
  1. 如1所示Lambda表达式中不含参数,使用空括号()标识没有参数。该Lambda 表达式实现了Runnable接口,该接口也只有一个run方法 , 没有参数,且返回类型为void类型。
  2. 如2所示的lambda表达式包含且只有一个参数,可以省略参数的括号,lambda表达式主题不仅可以是一个表达式,也可以是一段代码块,使用({})大括号将代码块括起来如3所示
  3. 如4所示,这是就有必要思考怎样去阅读该Lambda表达式。这行代码并不是将两数字相加,而是创建了一个函数,用来计算两个数字相加的结果,变量add的类型是BinaryOperator类型。

记住一点很重要,Lambda表达式都可以扩写为原始的匿名类形式,所以当你觉得这个Lambda表达式很负责且不易理解的时候,不妨把它扩写为 匿名类 形式来看

闭包

也许之前遇到过这样的问题,当匿名内部类需要所在方法里的变量的时候,必须把变量声明为 final , 如下所示:

        final int a = 1;
        new Runnable(){
            @Override
            public void run() {
                System.out.println(a);
            }
        };

java8 放松了这一限制,可以不必再把变量声明为 final,但其实该变量实际上仍然为final的。索然无需将变量声明为final , 但在lambda表达式中,也无法用作非终态变量, 如果坚持用作非终态变量(即改变变量的值),编译器就会报错。

函数式接口

上面例子中提到了 ActionListener 接口 , 我们看下它的代码:

public interface ActionListener extends EventListener {

    /**
     * Invoked when an action occurs.
     */
    public void actionPerformed(ActionEvent e);

}

ActionListener 只有一个抽象方法:actionPerformed,被用来表示行为:接受一个参数,返回空。记住,由于 actionPerformed 定义在一个接口里,因此 abstract 关键字不是必需的。该接口也继承自一个不具有任何方法的父接口:EventListener。

我们把这种接口就叫做函数接口。 JDK 8 中提供了一组常用的核心函数接口:

接口 参数 返回类型 描述
Predicate<T> T boolean 用于判别一个对象。比如求一个人是否为男性
Consumer<T> T void 用于接收一个对象进行处理但没有返回,比如接收一个人并打印他的名字
Function<T, R> T R 转换一个对象为不同类型的对象
Supplier<T> None T 提供一个对象
UnaryOperator<T> T T 接收对象并返回同类型的对象
BinaryOperator<T> (T, T) T 接收两个同类型的对象,并返回一个原类型对象
其中 ConsumerSupplier 对应,是一个消费者与一个提供者。
Predicate 用于判断对象是否符合某个条件,经常被用来过滤对象。
Function是将一个对象转换为另一个对象,比如说要装箱或拆箱某个对象。
UnaryOperator 接收和返回同类型对象,一般用于对对象属性进行修改。
BinaryOperator 可以理解为合并对象

集合处理

Stream

Java8 中引入流的概念, 这里的流和我们之前使用的IO流并不一样。 所有继承自Collection的接口都可以转换为Stream 。 假设我们有一个List中存放 Person对象,对象中有 名称 name , 年龄 age 两个字段, 现在求列表中年龄大于20的人数。 不用Stream 我们会这么写:

		int count = 0;
		for (Person person : persons) {
			if(person.getAge() > 20) {
				count ++;
			}
		}

如果用stream的话,则会简单很多:

		long count2 = persons.stream()
				.filter(person -> person.getAge() > 20)
				.count();

实际filter中参数转换为了如下函数进行的调用:

		Predicate predicate = new Predicate<Person>() {
			@Override
			public boolean test(Person o) {
				return o.getAge() > 20 ;
			}
		};
		long count1 = persons.stream()
				.filter(predicate)
				.count();

这是stream的很简单的一个用法,链式调用方法算是一个主流, 这样也更利于理解。

Stream的常用操作

Stream的方法分为两类, 一类叫惰性求值,一类叫及早求值。 判断一个操作是惰性求值还是及早求值很简单:只需看它的返回值 , 如果返回值是Stream 就是惰性求值。 如果调用惰性求值方法,Stream会记录下这个惰性求值方法的过程 , 并没有去计算, 等到及早求值方法后,就会联通一系列惰性求值方法顺序进行计算 , 返回结果。 通用形式为: Stream.惰性求值.惰性求值....惰性求值.及早求值。 整个过程和建造者模式有共通之处, 建造者模式使用一系列的操作配置属性和配置, 最后调用build方法创建对象。

collect(Collectors.toList())

collect(Collectors.toList()) 方法由 Stream 里的值生成一个列表,是一个及早求值操作。可以理解为 Stream 向 Collection 的转换。 注意这边的Collectors.toList() 其实采用了静态导入,看起来十分简洁。

List<String> collect = Stream.of("a", "b", "c").collect(Collectors.toList());

map

如果一个函数可以将一种类型的值转换成另外一种类型, map操作就可以使用该函数, 将一个流中的值转换为一个新的流

List<String> upperCollect = Stream.of("a", "b", "hello").map(item -> item.toUpperCase()).collect(Collectors.toList());

map方法就是接受一个 Function 的匿名函数进行转换:

		Function<String,String> function = new Function<String, String>() {
			@Override
			public String apply(String o) {
				return o.toUpperCase();
			}
		};
		List<String> funcCollect = Stream.of("a", "b", "hello").map(function).collect(Collectors.toList());

filter

遍历集合数据并过滤其中的元素 , 可尝试使用Stream 中提供的新方法 filter

		long count2 = persons.stream()
				.filter(person -> person.getAge() > 20)
				.count();

filter方法就是接收一个Predicate 的匿名函数类,判断对象是否符合条件 , 符合条件才保留下来。

		Predicate predicate = new Predicate<Person>() {
			@Override
			public boolean test(Person o) {
				return o.getAge() > 20 ;
			}
		};
		long count1 = persons.stream()
				.filter(predicate)
				.count();

flatMap

flatMap可用Stream替换 , 然后将多个Stream链接成一个Stream。

		List<Integer> collect = Stream.of(Arrays.asList(1, 2), Arrays.asList(3, 4))
				.flatMap(item -> item.stream()).collect(Collectors.toList());

flatMap常用的操作就是合并多个 Collection。

max 和 min

Stream 上常用的操作之一是求最大值和最小值, Stream API中的max 和 min操作足以解决这一问题。

		List<Integer> list = Arrays.asList(2,4,6,3,9,4);

		Integer maxInt = list.stream().max(Integer::compareTo).get();
		Integer minInt = list.stream().min(Integer::compareTo).get();

		System.out.println(maxInt);
		System.out.println(minInt);

这里有两点需要注意: 1.max 和 min 方法返回的是一个 Optional 对象 , (类似Google Guava里的 Optional对象一样) 。 Optional 对象封装的就是实际的值,可能为空 , 所以保险起见 , 可以先用 isPresent() 方法判断一下, Optional的引入就是为了解决方法返回 null 的问题。 2.Integer::compareTo 也是属于 Java8 引入的新特性 , 叫作 方法引入 (Method References) ,在这边就是 (int1,int2) -> int1.compareTo(int2) 的简写 。可以自己查阅,不做赘述。

reduce

reduce 操作可以实现从一组值中生成一个值。 在上述例子中用到的 count , min 和 max 方法 , 因为常用而被纳入标准库中 。 事实上 这些方法都是reduce 操作。 如下是一个累加的过程:

long l = Stream.of(1, 2, 3, 4 ).reduce(0, (acc, element) -> acc + element).longValue();

注意reduce的第一个参数 , 这是一个初始值 , 0 + 1 + 2+ 3 + 4 = 10; 实际第二个参数是BinaryOperator<T>的匿名内部类 如果累乘为如下形式

		BinaryOperator<Integer> operator = new BinaryOperator<Integer>() {
			@Override
			public Integer apply(Integer integer, Integer integer2) {
				return integer * integer2;
			}
		};
		long l1 = Stream.of(1, 2, 3, 4).reduce(1, operator).longValue();

结果为1 * 1 * 2 * 3 * 4 = 24;

© 著作权归作者所有

BakerZhu
粉丝 109
博文 517
码字总数 423077
作品 0
通州
程序员
私信 提问
加载中

评论(0)

Java8新特性:函数式接口@FunctionalInterface使用说明

Java8新特性:接口静态方法与默认方法 Java8新特性:函数式接口@FunctionalInterface使用说明 Java8新特性:方法引用 Java8新特性:函数式编程 我们常用的一些接口Callable,Runnable,Comparato...

键走偏锋
2018/08/05
370
0
资料 | 《 Java 8 实战 》

《 Java 8 实战 》 本书全面介绍了 Java 8 这个里程碑版本的新特性,包括 Lambdas、流和函数式编程。有了函数式的编程特性,可以让代码更简洁,同时也能自动化地利用多核硬件。全书分 4 个部...

AI研习社
2019/12/27
0
0
java8新特性(二)_lambda表达式

最近一直找java8相关新特性的文章,发现都太没有一个连贯性,毕竟大家写博客肯定都有自己的侧重点,这里找到一本书,专门介绍java8新特性的,感觉大家可以看看《写给大忙人看的JavaSE8》.这里...

阳光温暖
2018/08/04
0
0
Java8 lambda使用总结-结合实例介绍

1.Java8 新特性介绍 写java的同学对java8肯定知道 那么java8到底有哪些特性呢,总结如下: 最核心的当然是函数式编程了,写代码非常简单,请看下面详细例子介绍 2.Java8 lambda使用总结-结合...

陈小扁
2016/07/01
4.1K
4
Java系列 - 用Java8新特性进行Java开发太爽了

本人博客文章网址:https://www.peretang.com/using-java8s-new-features-to-coding-is-awesome/ 前言 从开始写博客到现在已经过去3个月了. 并且保持着每周更新一次的规律. 这挺好的, 但是每次...

PereTang
2017/07/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Flutter 强大的MediaQuery控件

注意:无特殊说明,Flutter版本及Dart版本如下: Flutter版本: 1.12.13+hotfix.5 Dart版本: 2.7.0 MediaQuery 通常情况下,不会直接将MediaQuery当作一个控件,而是使用MediaQuery.of获取当...

老孟程序员
33分钟前
28
0
【实战】2.如何写周报

如何写周报 一、周报的目的 以一个时间节点为准,同时做到向上汇报和向下汇报。向上汇报要做到整体项目的概况,让上级领导知道当前项目的整体状态。向下汇报要做到我们当前做了什么,紧接着的...

卖小女孩的小火柴
40分钟前
24
0
美颜重磅技术之GPUImage源码分析

说到基于GPU的图像处理和实时滤镜,大家肯定会想到鼎鼎大名的GPUImage,这个项目确实为后续开发提供了很多方便,基本的图像处理工具一应俱全。但是学习借鉴GPUImage的项目结构,可以为我们提...

码农突围
46分钟前
34
0
mapbox

Mapbox是一个可以跨行业使用的开发平台,我们可以利用它对地图进行创建和定制,以解决地图、数据和空间分析等问题。 Leaflet 轻量 WebGIS 前端类库 Leaflet 是一个为建设移动设备友好的互动地...

东东笔记
52分钟前
32
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部