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;
java8函数式编程实例的更多相关文章
- 关于Java8函数式编程你需要了解的几点
函数式编程与面向对象的设计方法在思路和手段上都各有千秋,在这里,我将简要介绍一下函数式编程与面向对象相比的一些特点和差异. 函数作为一等公民 在理解函数作为一等公民这句话时,让我们先来看一下一种非常常 ...
- Java8 函数式编程详解
Java8 函数式编程详解 Author:Dorae Date:2017年11月1日23:03:26 转载请注明出处 说起Java8,可能很多人都已经知道其最大的改进,就是引入了Lambda表达式与S ...
- Python进阶:函数式编程实例(附代码)
Python进阶:函数式编程实例(附代码) 上篇文章"几个小例子告诉你, 一行Python代码能干哪些事 -- 知乎专栏"中用到了一些列表解析.生成器.map.filter.lam ...
- Java8函数式编程探秘
引子 将行为作为数据传递 怎样在一行代码里同时计算一个列表的和.最大值.最小值.平均值.元素个数.奇偶分组.指数.排序呢? 答案是思维反转!将行为作为数据传递. 文艺青年的代码如下所示: public ...
- [2017.02.23] Java8 函数式编程
以前学过Haskell,前几天又复习了其中的部分内容. 函数式编程与命令式编程有着不一样的地方,函数式编程中函数是第一等公民,通过使用少量的几个数据结构如list.map.set,以及在这些数据结构上 ...
- [一] java8 函数式编程入门 什么是函数式编程 函数接口概念 流和收集器基本概念
本文是针对于java8引入函数式编程概念以及stream流相关的一些简单介绍 什么是函数式编程? java程序员第一反应可能会理解成类的成员方法一类的东西 此处并不是这个含义,更接近是数学上的 ...
- Java8函数式编程和lambda表达式
文章目录函数式编程JDK8接口新特性函数接口方法引用函数式编程函数式编程更多时候是一种编程的思维方式,是一种方法论.函数式与命令式编程区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉 ...
- 漫漫人生路,学点Jakarta基础-Java8函数式编程
接口默认方法 Java8版本以后新增了接口的默认方法,不仅仅只能包含抽象方法,接口也可以包含若干个实例方法.在接口内定义实例方法(但是注意需要使用default关键字) 在此定义的方法并非抽象方法,而 ...
- Java8函数式编程的宏观总结
1.java8优势通过将行为进行抽象,java8提供了批量处理数据的并行类库,使得代码可以在多核CPU上高效运行. 2.函数式编程的核心使用不可变值和函数,函数对一个值进行处理,映射成另一个值. 3. ...
随机推荐
- 2019冬季PAT甲级第四题
#define HAVE_STRUCT_TIMESPEC #include<bits/stdc++.h> using namespace std; typedef struct node{ ...
- js-秒数转为XX时XX分XX秒(用于计算剩余时间或倒计时)
export default { data() { return { hours: null, minute: null, second: null } }, methods: { // 秒数 转为 ...
- C# 重载与重写
重载(overload) 指调用同一方法名,但各方法中参数的数据类型.个数或顺序不同. public static int Add(int x,int y) { return x + y; } pub ...
- java篇 之 抽象
Abstract(抽象): Public abstract void work(); <==> public void work(){ } 抽象方法,存在于抽象类中, 提供一个方 ...
- MYSQL 传汉字获取拼音首字母
--获取单个汉字首字母拼音 --CREATE DEFINER=`by`@`%` FUNCTION `fun_first_pinyin`(`P_NAME` VARCHAR(5)) RETURNS var ...
- LVS+Keepalived -----> 效率最高的负载均衡器
一. 简介 1. 负载均衡的类型 负载均衡可以采用硬件设备(例如常常听见的 F5),也可以采用软件负载 商用硬件负载设备成本通常较高(一台几十万甚至上百万),所以一般 情况下会采用软件负载 软件负 ...
- Flask - app.debug=True,python manage.py和export FLASK_DEBUG=True,flask run的不同。
TL;DR,可以直接看下面的总结 问题1:为什么app.config['DEBUG'] = True,然后flask run并没有开启debugger和reloading,而直接运行脚本(python ...
- 容器远程访问vnc--CentOS 6.8安装和配置VNC
对于用惯了WIN系统的朋友来说,没有图形化操作界面的Linux用起来实在太难受了.实际上,Linux也是有图形化操作界面的,这就是VNC.接下来本文将告诉大家如何在CentOS 6.8下安装和配置 V ...
- 【代码总结】PHP面向对象之类与对象
一.类和对象的关系 类的实体化结果是对象,而对象的抽象就是类.在开发过程中,我们通常都是先抽象(幻想)出一个类,再用该类去创建对象(实现幻想的内容).在程序中,直接使用的是我们(实现幻想)的对象,而不 ...
- ZOJ1004 Anagrams by Stack
题目大意:规定 i 为入栈,o 为出栈,现在给两个字符串st1,st2,现在要将st1转化为st2,转化方法是,st1中字符从头开始入栈,并合理出栈构造出st2.请输出所有可能的出入栈步骤. 深度优先 ...