函数式编程概念,JAVA八新特性Lambda表达式和流(Stream)的使用。
一、基本概念
命令式编程:是一种描述计算机所需作出的行为的编程典范。主要思想是关注计算机执行的步骤,计算机则会严格遵循指令。
传统的硬件运行的机器码指令就是以命令式分格编写的。也就是对于需要实现的功能,要编写指令明确指出计算机应该如何实现。从冯诺依曼体系结构来看,就是CPU每次对这些指令取指、译码、执行,然后把结果写回内存。因此命令式编程就是对这些操作的抽象。
声明式编程:与命令式编程相对立,它描述目标的性质,让计算机明白目标,而非流程。即声明式编程不用告诉计算机问题领域,从而避免随之而来的副作用。而命令式编程则需要用算法来明确的指出每一步该怎么做。
声明式编程通常被看做是形式逻辑的理论,把计算看做推导。因此其通常由若干规范的声明组成,是人脑思维方式的抽象,利用数理逻辑或既定规范对已知条件进行推理或运算。典型的声明式编程语言如数据库查询语言SQL,只需要指明需要的数据和条件,底层实现由数据库完成。
函数式编程:是种编程方式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。
函数式编程是声明式编程的一部分,它们思想是一致的,即只关注做什么而不是怎么做。但函数式编程利用函数可作为参数的特点,使得函数可以出现在任何地方。同时其具有不可变性,即函数不存在副作用,如果要修改则需克隆新的备份数据。(如j = i ++,其中i ++ 获取到i的值,但是副作用是使得i的值加1)
二、Lambda表达式
Lambda表达式:相当于一个没有名字的函数,基本格式就是(参数)->(代码块)。->将参数和表达式主体分开,其相对于匿名内部类则无需显式指定参数类型。是一种更为紧凑的、传递行为的方式。
Lambda表达式除了基本形式外,还有不同的变体。表达式的参数类型都是由编译器推断得到。而Lambda表达式的目标类型则指表达式所在的上下文环境,将表达式赋给一个局部变量或方法参数,局部变量或方法参数就是其目标类型。
使用匿名内部类时,要引用它所在方法里的变量,就需要将变量声明为final。Lambda表达式内部使用外部变量同样需要是final或者是既成事实上的final类型。因为Lambda表达式引用的是值而非变量,因此声明为final就可以防止误解。
Lambda表达式本身的类型其实是一个接口,称其为函数接口。这种接口的特点就是只有一个抽象方法被用来表示行为,方法命名并不重要。只要方法签名和Lambda表达式类型匹配即可。函数接口中的单一方法参数可以自由指定,同时也可以使用泛型,通过类型推断得出具体类型。
三、Java中重要的函数接口
断言型接口Predicate<T>:Predicate接口返回一个Booolean的参数,该函数只有一个输入参数。接口中包含多种默认方法,用于处理复杂的逻辑动词。
功能型接口Function<T,R>:Function接口接收一个参数,并返回单一的结果。默认方法可以将多个函数串在一起。
功能型接口BinaryOperator<T>:和Function不同的是该接口接收两个参数,返回一个结果。
供给型接口Supplier<T>:Supplier接口产生一个给定类型的结果。与Function不同的是,Supplier没有输入参数。
消费型接口Consumer<T>:Consumer代表了在一个输入参数上需要进行的操作,函数返回值为void。
四、常用流操作
Java引入Stream API,对于传统集合的操作由外部迭代转为内部迭代,省去了多余循环模板。可以说Stream就是函数式编程方式在集合类上进行复杂操作的工具。
collect(toList())
该方法由Stream里的值生成一个列表,是一个及早求值的操作。其中toList()也可以改成toSet(),即将Stream里的值生成一个集合。
map
map操作接收一个Function接口实例,其作用就是一种类型构成的Stream转换为另一个中类型构成的Stream。完成转换工作的就是Fuction接口实例。
filter
filter操作接收一个Predicate接口实例,其作用就是起到过滤的作用。对于由许多值构成的Stream,对于其中的每个值经过Predicate接口函数都会返回true或者false值,而经过filter操作的Stream就过滤了所有结果为false的值,最终所有经Predicate接口计算为true的值构成新的Stream返回。
flatMap
flatMap和map操作很像,也是接收一个Function接口实例。不同之处在于flatMap将原Stream的每个值转换为一个Stream,且其还负责将这些转换的Stream连接成一个Stream。
max和min
max和min操作就是求最大值和最小值,操作接收的是Comparator接口实例,同时返回一个Optional实例。Optional对象的作用就是表示一个可能存在也可能不存在的值,如Stream为空那么值就不存在,调用get的方法就可以获取值且内部还对空值做了检测。
reduce
该操作可以实现从一组值中生成一个值,可以发现count、max、min操作都是reduce操作。reduce操作接收一个初值和BinaryOperator实例,每次将计算结果累加到初值,得到一个最终结果。
五、高级集合类和收集器
方法引用:方法引用是对Lambda进一步简写,可以重用已有方法。且和常规的方法调用不同,使用方法引用时可以省去括号。(如创建对象可以表示成String::new)
之前使用collect(toList())将Stream转换为列表,其中toList()就是一个收集器,类似的还有toSet()、toCollection()。这些收集器将Stream转换为其他集合。类似的还有maxBy、minBy可以将Stream转换成值的收集器,将比较器传入这些收集器,在将收集器传给collect就可以实现转换。收集器averagingInt则可以求出平均值。收集器Collectors.joining是针对字符串流,将流中所有字符串指定分隔符、前缀、后缀进行拼接成一个字符串。
还有另外一些收集器可以实现将数据分块,如partitioningBy收集器,接收一个Predicate接口实例,并将该收集器传给collect就可以将流的数据分成两部分。结果为true的一组和为false的一组并以Map<Boolean, List<T>>的形式返回。
处理数据分块收集器,还有数据分组收集器即groupingBy,相比于partitioningBy其支持任意值对数据进行分组。因此groupingBy接收的是Function接口实例,分组结果以Map<T, List<R>>的形式展现。
六、并行化流操作
并行和并发:并发是指两个任务共享时间段,并行则是两个任务在同一个时间发生。并发通常发生在一个CPU给两个任务分配时间片交叉执行,而并行多发生在多核CPU上。
在Stream的设计中,调用其parallel方法就能让其拥有并行操作的能力,而对于集合调用parallelStream也可以获得一个拥有并行能力的流。相比于串行流、并行流在reduce操作上有所限制。且并行化流性能并不总优于串行流,通常并行化流在简单操作处理大量数据上发挥的性能更好。
标签:JAVA,函数,Stream,收集器,编程,接口,表达式 From: https://www.cnblogs.com/idempotent/p/12168877.html