java8函数式编程实例
什么是函数式编程
函数式编程是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;
浙公网安备 33010602011771号