一、函数式编程
1.1 概述
目的: 为了提高效率,减少工作量,减少bug。
概念:首先需要清楚一个概念:
函数式接口;它指的是有且只有一个未实现的方法的接口,一般通过FunctionalInterface这个注解来表明某个接口是一个函数式接口。
函数式接口是Java支持函数式编程的基础。这些接口都在java包中:
具体简化实例(以Consumer为例子)
1
2
3
4
5
6
7
8
9
|
//前
Consumer c = new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o);
}
};
//后
Consumer c = o -> System.out.println(o);
|
1.2 常用接口及其介绍
1.2.1 Predicate
predicate的中文意思是“断定”,即判断的意思,判断某个东西是否满足某种条件。源代码为:
1
2
3
4
|
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
|
因此它包含test方法,根据输入值来做逻辑判断,其结果为True或者False。它的使用方法示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/**
* Predicate测试
*/
private static void predicateTest() {
Predicate<String> p = o -> o.equals("test");
Predicate<String> g = o -> o.startsWith("t");
/**
* negate: 用于对原来的Predicate做取反处理;
* 如当调用p.test("test")为True时,调用p.negate().test("test")就会是False;
*/
Assert.assertFalse(p.negate().test("test"));
/**
* and: 针对同一输入值,多个Predicate均返回True时返回True,否则返回False;
*/
Assert.assertTrue(p.and(g).test("test"));
/**
* or: 针对同一输入值,多个Predicate只要有一个返回True则返回True,否则返回False
*/
Assert.assertTrue(p.or(g).test("ta"));
}
|
1.2.2 Function
Function也是一个函数式编程接口;它代表的含义是“函数”,而函数经常是有输入输出的,因此它含有一个apply方法,包含一个输入与一个输出;
除apply方法外,它还有compose与andThen及indentity三个方法,其使用见下述示例;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
/**
* Function测试
*/
public static void functionTest() {
Function<Integer, Integer> f = s -> ++s;
Function<Integer, Integer> g = s -> s * 2;
/**
* 下面表示在执行F时,先执行G,并且执行F时使用G的输出当作输入。
* 相当于以下代码:
* Integer a = g.apply(1);
* System.out.println(f.apply(a));
* 结果为:3
*/
System.out.println(f.compose(g).apply(1));
/**
* 表示执行F的Apply后使用其返回的值当作输入再执行G的Apply;
* 相当于以下代码
* Integer a = f.apply(1);
* System.out.println(g.apply(a));
* 结果为:4
*/
System.out.println(f.andThen(g).apply(1));
/**
* identity方法会返回一个不进行任何处理的Function,即输出与输入值相等;
* 结果为:a
*/
System.out.println(Function.identity().apply("a"));
}
|
1.2.3 Consumer 和 Supplier
Consumer是一个函数式编程接口; 顾名思义,Consumer的意思就是消费,即针对某个东西我们来使用它,因此它包含有一个有输入而无输出的accept接口方法;
除accept方法,它还包含有andThen这个方法;
Supplier是一个函数式编程接口;与Consumer相对,Supplier是生产者的意思,即凭空生产出一个东西,因此它包含一个没有输入而有输出的Get()接口方法;
1
2
3
4
5
6
7
8
9
10
|
public static void consumerTest() {
Consumer f = System.out::println;
Consumer f2 = n -> System.out.println(n + "-F2");
//执行完F后再执行F2的Accept方法
f.andThen(f2).accept("test");
//连续执行F的Accept方法
f.andThen(f).andThen(f).andThen(f).accept("test1");
}
|
1.2.4 其他接口
以上四个接口为常用的基本函数式接口,除此之外还有一些扩展:
- BiConsumer、DoubleConsumer、IntConsumer、LongConsumer、ObjIntConsumer、ObjLongConsumer
- LongSupplier、IntSupplier、DoubleSupplier、BooleanSupplier
- UnaryOperator、LongUnaryOperator、LongBinaryOperator、IntUnaryOperator、IntBinaryOperator、DoubleUnaryOperator、DoubleBinaryOperator、
ToLongFunction、ToLongBiFunction、ToIntFunction、ToIntBiFunction、ToDoubleFunction、BinaryOperator、BiFunction
ToDoubleBiFunction、LongToIntFunction、LongToDoubleFunction、IntToLongFunction、IntToDoubleFunction、IntFunction、DoubleToLongFunction、
DoubleToIntFunction、DoubleFunction、
- LongPredicate、IntPredicate、DoublePredicate、BiPredicate
二、函数式接口的使用
通过Stream以及Optional两个类,可以进一步利用函数式接口来简化代码。
2.1 Stream
Stream可以对多个元素进行一系列的操作,也可以支持对某些操作进行并发处理。
2.1.1 Stream对象的创建
Stream对象的创建途径有以下几种:
- 创建空的Stream对象
1
|
Stream stream = Stream().Empty();
|
- 通过Stream中的of方法创建
1
2
3
|
List<String> list = Arrays.asList("a", "b", "c", "d");
Stream listStream = list.stream(); //获取串行的Stream对象
Stream parallelListStream = list.parallelStream(); //获取并行的Stream对象
|
- 通过Stream中的iterate方法创建:
iterate方法有两个不同参数的方法
1
2
|
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f);
public static<T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
|
其中第一个方法将会返回一个无限有序值的Stream对象:它的第一个元素是seed,第二个元素是f.apply(seed); 第N个元素是f.apply(n-1个元素的值);生成无限值的方法实际上与Stream的中间方法类似,在遇到中止方法前一般是不真正的执行的。因此无限值的这个方法一般与limit等方法一起使用,来获取前多少个元素。
当然获取前多少个元素也可以使用第二个方法。
第二个方法与第一个方法生成元素的方式类似,不同的是它返回的是一个有限值的Stream;中止条件是由hasNext来断定的。
- 通过Stream中的generate方法创建
与iterate中创建无限元素的Stream类似,不过它的每个元素与前一元素无关,且生成的是一个无序的队列。也就是说每一个元素都可以随机生成。因此一般用来创建常量的Stream以及随机的Stream等。
示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
/**
* 随机生成10个Double元素的Stream并将其打印
*/
Stream.generate(new Supplier<Double>() {
@Override
public Double get() {
return Math.random();
}
}).limit(10).forEach(System.out::println);
//上述写法可以简化成以下写法:
Stream.generate(() -> Math.random()).limit(10).forEach(System.out::println);
|
- 通过Stream中的concat方法连接两个Stream对象生成新的Stream对象
2.1.2 Stream对象的使用
Stream对象提供多个非常有用的方法,这些方法可以分成两类:
中间操作:将原始的Stream转换成另外一个Stream;如filter返回的是过滤后的Stream。
终端操作:产生的是一个结果或者其它的复合操作;如count或者forEach操作。
所有中间操作
方法 |
说明 |
sequential |
返回一个相等的串行的Stream对象,如果原Stream对象已经是串行就可能会返回原对象 |
parallel |
返回一个相等的并行的Stream对象,如果原Stream对象已经是并行的就会返回原对象 |
unordered |
返回一个不关心顺序的Stream对象,如果原对象已经是这类型的对象就会返回原对象 |
onClose |
返回一个相等的Steam对象,同时新的Stream对象在执行Close方法时会调用传入的Runnable对象 |
close |
返回void,关闭Stream对象 |
filter |
元素过滤:对Stream对象按指定的Predicate进行过滤,返回的Stream对象中Predicate为true的元素 |
map |
元素一对一转换:使用传入的Function对象对Stream中的所有元素进行处理,返回的Stream对象中的元素为原元素处理后的结果 |
mapToInt |
元素一对一转换:将原Stream中的使用传入的IntFunction加工后返回一个IntStream对象 |
flatMap |
元素一对多转换:对原Stream中的所有元素进行操作,每个元素会有一个或者多个结果,然后将返回的所有元素组合成一个统一的Stream并返回; |
distinct |
去重:返回一个去重后的Stream对象 |
sorted |
排序:返回排序后的Stream对象 |
peek |
使用传入的Consumer对象对所有元素进行消费后,返回一个新的包含所有原来元素的Stream对象 |
limit |
获取有限个元素组成新的Stream对象返回 |
skip |
抛弃前指定个元素后使用剩下的元素组成新的Stream返回 |
takeWhile |
如果Stream是有序的(Ordered),那么返回最长命中序列(符合传入的Predicate的最长命中序列)组成的Stream;如果是无序的,那么返回的是所有符合传入的Predicate的元素序列组成的Stream。 |
dropWhile |
与takeWhile相反,如果是有序的,返回除最长命中序列外的所有元素组成的Stream;如果是无序的,返回所有未命中的元素组成的Stream。 |
所有终端操作
方法 |
说明 |
iterator |
返回Stream中所有对象的迭代器; |
spliterator |
返回对所有对象进行的spliterator对象 |
forEach |
对所有元素进行迭代处理,无返回值 |
forEachOrdered |
按Stream的Encounter所决定的序列进行迭代处理,无返回值 |
toArray |
返回所有元素的数组 |
reduce |
使用一个初始化的值,与Stream中的元素一一做传入的二合运算后返回最终的值。每与一个元素做运算后的结果,再与下一个元素做运算。它不保证会按序列执行整个过程。 |
collect |
根据传入参数做相关汇聚计算 |
min、max |
返回所有元素中最小(大)值的Optional对象;如果Stream中无任何元素,那么返回的Optional对象为Empty |
count |
所有元素个数 |
anyMatch |
只要其中有一个元素满足传入的Predicate时返回True,否则返回False |
allMatch |
所有元素均满足传入的Predicate时返回True,否则False |
noneMatch |
所有元素均不满足传入的Predicate时返回True,否则False |
findFirst |
返回第一个元素的Optional对象;如果无元素返回的是空的Optional; 如果Stream是无序的,那么任何元素都可能被返回。 |
findAny |
返回任意一个元素的Optional对象,如果无元素返回的是空的Optioanl。 |
isParallel |
判断是否当前Stream对象是并行的 |
2.1.2.1 常用的方法举例
- filter
1
2
3
4
5
6
|
//定义
Stream<T> filter(Predicate<? super T> predicate);
//查找所有包含t的元素并进行打印
Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa");
s.filter(n -> n.contains("t")).forEach(System.out::println);
|
- map
1
2
3
4
5
|
//定义
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
//每个元素添加相同的后缀.txt
Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa");
s.map(n -> n.concat(".txt")).forEach(System.out::println);
|
- flatMap
1
2
3
4
5
|
//定义
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
//将每一个元素的拆分成单个字母,并打印
Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa");
s.flatMap(n -> Stream.of(n.split(""))).forEach(System.out::println);
|
- takeWhile
1
2
3
4
5
6
|
//定义
default Stream<T> takeWhile(Predicate<? super T> predicate)
//查找这几个元素中包含t的最长命中序列
Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa", "taaa");
//以下结果将打印: "test", "t1", "t2", "teeeee",最后的那个taaa不会进行打印
s.takeWhile(n -> n.contains("t")).forEach(System.out::println);
|
- dropWhile
1
2
3
4
5
6
|
//定义
default Stream<T> dropWhile(Predicate<? super T> predicate)
//删除这几个元素中包含t的最长命中序列
Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa", "taaa");
//以下结果将打印:"aaaa", "taaa"
s.dropWhile(n -> n.contains("t")).forEach(System.out::println);
|
2.2 Optional
用于简化Java中对空值的判断处理,以防止出现各种空指针异常。
Optional实际上是对一个变量进行封装,它包含有一个属性value,实际上就是这个变量的值。
2.2.1 Optional对象创建
- empty
用于创建一个空的Optional对象;其value属性为Null。
1
|
Optional o = Optional.empty();
|
- of
根据传入的值构建一个Optional对象;
传入的值必须是非空值,否则如果传入的值为空值,则会抛出空指针异常。
1
|
o = Optional.of("test");
|
- ofNullable
根据传入值构建一个Optional对象
传入的值可以是空值,如果传入的值是空值,则与empty返回的结果是一样的。
2.2.2 方法
方法 |
描述 |
get |
获取Value的值,如果Value值是空值,则会抛出NoSuchElementException异常;因此返回的Value值无需再做空值判断,只要没有抛出异常,都会是非空值 |
isPresent |
Value是否为空值的判断 |
ifPresent |
当Value不为空时,执行传入的Consumer; |
ifPresentOrElse |
Value不为空时,执行传入的Consumer;否则执行传入的Runnable对象; |
filter |
当Value为空或者传入的Predicate对象调用test(value)返回False时,返回Empty对象;否则返回当前的Optional对象 |
map |
一对一转换:当Value为空时返回Empty对象,否则返回传入的Function执行apply(value)后的结果组装的Optional对象; |
flatMap |
一对多转换:当Value为空时返回Empty对象,否则传入的Function执行apply(value)后返回的结果(其返回结果直接是Optional对象) |
or |
如果Value不为空,则返回当前的Optional对象;否则,返回传入的Supplier生成的Optional对象; |
stream |
如果Value为空,返回Stream对象的Empty值;否则返回Stream.of(value)的Stream对象; |
orElse |
不为空则返回Value,否则返回传入的值; |
orElseGet |
Value不为空则返回Value,否则返回传入的Supplier生成的值; |
orElseThrow |
Value不为空则返回Value,否则抛出Supplier中生成的异常对象; |
2.2.3 使用场景
- 判断结果不为空后使用
1
2
3
4
5
6
7
8
9
|
//以前的做法
String s = test();
if (null != s) {
System.out.println(s);
}
//现在做法
Optional<String> s = Optional.ofNullable(test());
s.ifPresent(System.out::println);
|
- 变量为空时提供默认值
1
2
3
4
5
6
7
8
9
|
//以前的做法
if (null == s) {
s = "test";
}
System.out.println(s);
//现在做法
Optional<String> o = Optional.ofNullable(s);
System.out.println(o.orElse("test"));
|
- 变量为空时抛出异常,否则使用
1
2
3
4
5
6
7
8
9
|
//以前的做法
if (null == s) {
throw new Exception("test");
}
System.out.println(s);
//现在做法
Optional<String> o = Optional.ofNullable(s);
System.out.println(o.orElseThrow(()->new Exception("test")));
|