什么是函数式编程
函数式编程是java8的一大特色,也就是将函数作为一个参数传递给指定方法。别人传的要么是基本数据类型,要么就是地址引用 ,我们要穿一个“动作”。
Stream
说到函数式编程,就不得不提及Stream,Stream跟我们熟知的io流可不是同一个东西,泛指可以顺序执行或者并行执行的元素序列,主要是针对集合,可以将多个函数通过“.”串起来执行,其特点如下:
- stream不会存储数据,只是将集合流化,比如说 声明一个stream之后,往集合里面扔东西,stream可以取到新扔到集合里面的数据,你可以理解为操作时stream会实时从堆中的集合对象提取数据。
- stream不会改变原集合,我的理解是stream 是一堆元素顺序或者并行执行我们串起来的函数,改变后并不会对集合中的元素造成影响。
- steam是延迟执行的,也就是说在聚合操作之前 的其他操作,都会阻塞,直到执行聚合函数,其他的函数才开始一并执行。
解析相关接口
我们看看跟函数式编程相关的接口
java.long.FunctionalInterface是一个注解接口,函数接口都会实现它,看看它有什么特别的
从这里我们可以看出来一个函数接口只有一个抽象方法,但是如果要加一切其他的功能怎么办呢?接口中添加功能的话相当麻烦,接口相关实现类都需要修改,接下来“default”就登场了
default方法只能在接口出现,它不是抽象方法,可以通过methodInstance.isDefault()辨认,咦,弄啥勒,接口还能有普通方法?是的你没看错,添加default方法是为了方便修改接口,不至于每次修改过后都要将他的“儿子,孙子”等实现一一修改;如果在接口中default修饰的方法不加body会怎么样呢? 没错, 会编译失败!贴图:
接口中的default方法不用强制在实现类中 出现,当然业务需要的话,也可以重写来进行多态;
下面是java.util.function中的函数接口描述
序号 | 接口 & 描述 |
---|---|
1 | BiConsumer<T,U>
代表了一个接受两个输入参数的操作,并且不返回任何结果 |
2 | BiFunction<T,U,R>
代表了一个接受两个输入参数的方法,并且返回一个结果 |
3 | BinaryOperator<T>
代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果 |
4 | BiPredicate<T,U>
代表了一个两个参数的boolean值方法 |
5 | BooleanSupplier
代表了boolean值结果的提供方 |
6 | Consumer<T>
代表了接受一个输入参数并且无返回的操作 |
7 | DoubleBinaryOperator
代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。 |
8 | DoubleConsumer
代表一个接受double值参数的操作,并且不返回结果。 |
9 | DoubleFunction<R>
代表接受一个double值参数的方法,并且返回结果 |
10 | DoublePredicate
代表一个拥有double值参数的boolean值方法 |
11 | DoubleSupplier
代表一个double值结构的提供方 |
12 | DoubleToIntFunction
接受一个double类型输入,返回一个int类型结果。 |
13 | DoubleToLongFunction
接受一个double类型输入,返回一个long类型结果 |
14 | DoubleUnaryOperator
接受一个参数同为类型double,返回值类型也为double 。 |
15 | Function<T,R>
接受一个输入参数,返回一个结果。 |
16 | IntBinaryOperator
接受两个参数同为类型int,返回值类型也为int 。 |
17 | IntConsumer
接受一个int类型的输入参数,无返回值 。 |
18 | IntFunction<R>
接受一个int类型输入参数,返回一个结果 。 |
19 | IntPredicate
:接受一个int输入参数,返回一个布尔值的结果。 |
20 | IntSupplier
无参数,返回一个int类型结果。 |
21 | IntToDoubleFunction
接受一个int类型输入,返回一个double类型结果 。 |
22 | IntToLongFunction
接受一个int类型输入,返回一个long类型结果。 |
23 | IntUnaryOperator
接受一个参数同为类型int,返回值类型也为int 。 |
24 | LongBinaryOperator
接受两个参数同为类型long,返回值类型也为long。 |
25 | LongConsumer
接受一个long类型的输入参数,无返回值。 |
26 | LongFunction<R>
接受一个long类型输入参数,返回一个结果。 |
27 | LongPredicate
R接受一个long输入参数,返回一个布尔值类型结果。 |
28 | LongSupplier
无参数,返回一个结果long类型的值。 |
29 | LongToDoubleFunction
接受一个long类型输入,返回一个double类型结果。 |
30 | LongToIntFunction
接受一个long类型输入,返回一个int类型结果。 |
31 | LongUnaryOperator
接受一个参数同为类型long,返回值类型也为long。 |
32 | ObjDoubleConsumer<T>
接受一个object类型和一个double类型的输入参数,无返回值。 |
33 | ObjIntConsumer<T>
接受一个object类型和一个int类型的输入参数,无返回值。 |
34 | ObjLongConsumer<T>
接受一个object类型和一个long类型的输入参数,无返回值。 |
35 | Predicate<T>
接受一个输入参数,返回一个布尔值结果。 |
36 | Supplier<T>
无参数,返回一个结果。 |
37 | ToDoubleBiFunction<T,U>
接受两个输入参数,返回一个double类型结果 |
38 | ToDoubleFunction<T>
接受一个输入参数,返回一个double类型结果 |
39 | ToIntBiFunction<T,U>
接受两个输入参数,返回一个int类型结果。 |
40 | ToIntFunction<T>
接受一个输入参数,返回一个int类型结果。 |
41 | ToLongBiFunction<T,U>
接受两个输入参数,返回一个long类型结果。 |
42 | ToLongFunction<T>
接受一个输入参数,返回一个long类型结果。 |
43 | UnaryOperator<T>
接受一个参数为类型T,返回值类型也为T。 |
除了上面列出的,还有个别函数接口没列出来,例如Comparator(), 但总归是个函数接口都会基于FunctionalInterface注解
总结
这些函数主要还是分为几个主要函数,其余都是以其为基础的分化;
- Supplier<T> 无参数,返回一个结果。
- Consumer<T> 代表了接受一个输入参数并且无返回的操作。
- Function<T,R> 接受一个输入参数,返回一个结果,返回的结果跟参数的类型无关。
- Predicate<T> 接受一个输入参数,返回一个布尔值结果。
- BinaryOperator<T> 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果,xxxOperator 函数都是接受类型跟返回值类型相同。
而且,从上面五个主要函数接口命名也可以看出其作用,"Bi"作为前缀的都是属于二元操作,接受两个参数。
T1toT2是从接受T1类型参数,返回T2类型结果,比如IntToLongFunction 接受一个int类型参数,返回long类型结果。
实战1
下面我写了一个功能,拳击手有分组跟体重两个属性,然后给一组拳击手集合,根据小组或者体重级排序,用到的是Comparator(),也是一个函数接口
上码:
/**
* Created by zhanghe
* 拳击手
*/
public class Boxer {
//分组
private String group;
//重量级
private Integer weight;
public Boxer(String group, Integer weight) {
this.group = group;
this.weight = weight;
}
public static <T> List<List<T>> divider(List<T> datas, Comparator<T> c) {
//声明一个列表来接收各个分组
List<List<T>> list = new ArrayList<>();
for (T t : datas) {
//通过isSameGroup 标识来判断分组是否已创建
boolean isSameGroup = false;
for (int i = 0; i < list.size(); i++) {
//compare函数返回值为int,正数说明param1>param2,0说明param1=param2,负数说明param1<param2
//这里用到的原理是将List列表datas中的各项与分组列表list中的元素比较(比较的规则由外面作为参数传递,这就是函数式编程),
// 值为0表示规则相符即为同一组
if (c.compare(t, list.get(i).get(0)) == 0) {
isSameGroup = true;
list.get(i).add(t);
break;
}
}
//比较完了发现没有规则相符的,即自成一系
if (!isSameGroup) {
List<T> e = new ArrayList<>();
e.add(t);
list.add(e);
}
}
return list;
}
public static void main(String[] args) {
List<Boxer> boxers = Arrays.asList(
new Boxer("红队", 120),
new Boxer("绿队", 180),
new Boxer("蓝队", 200),
new Boxer("绿队", 220),
new Boxer("蓝队", 120),
new Boxer("红队", 80),
new Boxer("红队", 90),
new Boxer("绿队", 240)
);
List<List<Boxer>> dividByGroup = divider(boxers, new Comparator<Boxer>() {
@Override
public int compare(Boxer o1, Boxer o2) {
//分组一样 即认为 相同
return o1.group.equals(o2.group) == true ? 0 : 1;
}
});
System.out.println("根据小组区分:");
dividByGroup.stream().forEach(e->{
System.out.println(e);
});
List<List<Boxer>> dividByWeight = divider(boxers, new Comparator<Boxer>() {
@Override
public int compare(Boxer o1, Boxer o2) {
//体重/100 即体重百分位相同 即认为 相同
return (o1.weight / 100 - o2.weight / 100) == 0 ? 0 : 1;
}
});
System.out.println("根据体重区分:");
dividByWeight.stream().forEach(e->{
System.out.println(e);
});
}
@Override
public String toString() {
return "Boxer{" +
"小组=‘" + group + ‘\‘‘ +
", 体重=" + weight +
‘}‘;
}
}
这个是结果
实战2
我用Predicate 写了个具有匹配功能的方法,当然 匹配的规则还是作为一个函数参数传递,撸码不是很优雅,只看功能就好~
闲话不多说,上码:
List<Object> list = Arrays.asList(1, 2,"",100,"3", ‘c‘);
List<Object> matchList = match(list, a -> {
return a instanceof Integer && (Integer) a > 10;
});
System.out.println(matchList);//100
Set set = new HashSet<>();
set.add("haha");
set.add(1);
set.add(100);
Set matchSet = match(set, a -> a instanceof Integer);
System.out.println(matchSet);//[1, 100]
String matchString = match("java", a -> {
return a instanceof String && ((String) a).startsWith("j");
});
System.out.println(matchString);//java
Integer noMatch = match(100, a -> {
return a instanceof String && ((String) a).startsWith("j");
});
System.out.println(noMatch);//null
}
/**
*
* @param t 入参 需要匹配规则的参数
* @param p 函数接口 匹配的动作
* @param <T>
* @return 验证成功 是集合 则只留匹配元素,是String,Integer ..匹配成功返回本身,反之返回null
*/
public static <T> T match(T t, Predicate p) {
if (t instanceof Collection) {
if (t instanceof List) {
t = (T) ((List) t).stream().filter(a -> p.test(a)).collect(Collectors.toList());
}
if (t instanceof Set) {
t = (T) ((Set) t).stream().filter(a -> p.test(a)).collect(Collectors.toSet());
}
} else {
if (!p.test(t)) {
return null;
}
}
return t;
原文地址:https://www.cnblogs.com/liuys635/p/11012723.html