函数式编程
首先我们大家见过不少函数这个概念了。尤其是大学基础课C语言的时候,我们就学到了函数,学到了面向过程编程。那什么是函数式编程呢?函数式编程是一种编程范式,它将计算看作是数学函数的求值过程。函数式编程的核心思想是将计算过程分解为一系列的函数调用,而不是通过改变状态来实现计算。函数式编程强调函数的纯粹性和不可变性,函数的返回值只取决于输入参数,不会受外部状态的影响,并且函数执行过程中不会对外部状态产生副作用。函数式编程还提倡使用高阶函数,即函数可以作为参数传递给其他函数,或者作为返回值返回给其他函数。这种编程范式可以使代码更加简洁、模块化和可复用,同时有助于并发编程和并行计算。函数式编程在编程语言中有一些特定的实现,如Haskell、Lisp和Clojure等。
这个看起来是不是脑袋都大了,我也看的有点迷了,我的理解就是函数式编程就是,允许我们传入一个函数本身,经过处理之后,返回一个函数。
因为函数式编程最早是阿隆佐·邱奇研究的函数变换,又称lambda λ变换。 Java自Java8开始支持函数式编程。
Lambda表达式
- 通过接口传递代码
之前我们学习sort算法的时候,我们对一些自定义对象进行排序的时候,我们需要把排序的规则传递给sort,这个时候我们就是通过匿名内部类进行传递,实际上就是传递的比较的代码
Arrays.sort(files, new Comparator<File>() {
@Override
public int compare(File f1, File f2) {
return f1.getName().compareTo(f2.getName());
}
});
- Lambda语法
Java8 提供了Lambda表达式,对于上面的代码,我们改用Lambda写,如下
Arrays.sort(files, (f, f1) -> {
return f.getName().compareTo(f1.getName());
});
我们可以看到使用Lambda表达式可以简化代码,参数(f, f1) 的类型我们都不用写,为什么? 因为这个地方接受的是一个Comparator接口这个接口,编译器可以自动推断出是String类型。-> { } 这个地方写方法体。
- FunctionalInterface
我们把只定义单个方法的接口称为FunctionalInterface,用@FunctionalInterface注解标记
package java.lang;
@FunctionalInterface
public interface Runnable {
void run();
}
常见的function
Java 8定义了大量的预定义函数式接口,用于常见类型的代码传递,这些函数定义在包java.util.function下面。
** 代码演示**
我们展示一下function下面的一些接口的使用,方便大家理解。
public class Test21 {
public static void main(String[] args) {
List<Dog> dogs = Arrays.asList(new Dog[]{new Dog(1, "wangcai"), new Dog(2, "dahuang")});
dogs = filter(dogs, dog -> dog.age < 2);
System.out.println(dogs.size());
dogs = filter(dogs, dog -> dog.name.length() > 6);
System.out.println(dogs.size());
}
public static <E> List<E> filter(List<E> list, Predicate<E> predicate) {
List<E> ret = new ArrayList<>();
for (E e : list) {
if (predicate.test(e)) {
ret.add(e);
}
}
return ret;
}
}
class Dog{
public int age;
public String name;
public Dog(int age, String name) {
this.age = age;
this.name = name;
}
}
我们简单讲一下这段代码,其实我们关注的核心应该在通过使用function传递代码,这样我们写出了一个通用的方法。这样的话我们的filter可以灵活的传入多个过滤条件。
使用Stream
针对常见的集合数据处理,Java8引入了Stream的概念。Stream用于存储顺序输出的Java对象,用于我们的计算或者业务。那它跟我们的容器由什么区别,为啥要引入这个东西呢?
Stream是实时计算出来的,里面的数据可能未分配内存。(要注意我们Lambda表达式处理的是函数到函数的变换,如果我们现在要处理的是有理数集合,我们简单的之前的容器就承载不了,就比较适合使用容器)
如何创建Stream
public class Test22 {
public static void main(String[] args) {
// 直接基于Stream.of
Stream<Integer> stream = Stream.of(1, 2, 3, 4);
// 利用容器
Arrays.stream(new Integer[]{1, 2, 3, 4});
// 基于Supplier
Stream.generate(MySupplier::new);
}
}
class MySupplier implements Supplier<Integer> {
int count = 0;
@Override
public Integer get() {
return count++;
}
}
代码都写好了注释,看注释就可以了。
中间操作
Stream API中间操作有filter和map还有distinct、sorted、skip、limit、peek、mapToLong、mapToInt、mapToDouble、flatMap等
- distinct 用来去重的
- sorted 用来排序
- skip 跳过
- limit 限定多个
- peek 返回元素
- mapToLong、mapToInt、mapToDouble 对数据进行映射处理
- flatMap 简单理解就是1-n的映射
终端操作
Stream API的终端操作有collect、max、min、count、allMatch、anyMatch、noneMatch、findFirst、findAny、forEach、toArray、reduce等
- collect 收集
- max/min 最大最小
- allMatch 所有匹配的
- anyMatch 任何一个匹配的
- noneMatch 不匹配的
- findFirst 找到第一个
- findAny 找到任意一个
- forEach 循环
- toArray 转换成数组