java函数式编程之Collector、Optional、CompletableFuture详解
1. Stream.collect()
collect就是一个归约操作,就像reduce一样可以接受各种做法作为参数,将流中的元素累积成一个汇总结果
1.1 collect和reduce的区别
reduce不会修改累计值对象,而是直接把累计值和当前值做计算操作,产生一个新累计值对象传递下去,而collect每次都会做完操作后修改累计值对象,并把这个累计值对象传递下去,如下图,collect的累计值是一个Container,每次都把当前元素做完操作后,存入Container,并让Container一直往下传递,最后所有的结果都会汇总到Container中
1.2 collect vs reduce伪代码示意
1.3 collect(Supplier, Accumulator, Combiner)
1.3.1 Supplier
用于提供一个累计容器,也就是上图中用于存储元素的Container,通过调用Supplier的get方法得到
1.3.2 Accumulator
用于提供累计函数,即给定累计值和当前值的操作关系
1.3.3 Combiner
用于给定容器合并的规则
1.4 collect(Collector)
Collector就是把collect()方法中需要的函数封装成一个对象
2. Collector
2.1 Collector 要素
Supplier: 累积数据构造函数
Accumulator: 累积函数,同 reduce
Combiner: 合并函数 ,并行处理场合下用,同 reduce
Finisher: 对 累积数据 做最终 转换
//Collector 就是一个接口,带了几个静态方法,注意,接口也可以带default或者static方法
public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
BinaryOperator<A> combiner();
Function<A, R> finisher();
Set<Characteristics> characteristics();
public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,
BiConsumer<R, T> accumulator,
BinaryOperator<R> combiner,
Characteristics... characteristics) {
....
}
Collectors类就提供了一些api来创建Collector,如我们常用的toList源码如下
public static <T> Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}
可以看出来返回的这个Collector的Supplier是ArrayList::new,accumulator是List::add,combiner是(left, right) -> { left.addAll(right); return left; },没有finisher
3. Collectors API
3.1 toList /toConcurrentMap toSet toCollection
把结果归集为一个集合
List<Integer> list = Arrays.asList(0, 1, 2, 3, 4);
List<Integer> nums = list.stream().filter(i -> i > 0).collect(Collectors.toList());
3.2 counting/ averagingXX / summingXX
counting:统计计数
List<Integer> list = Arrays.asList(0, 1, 2, 3, 4);
long count = list.stream().collect(Collectors.counting());
System.out.println(count);//5
averagingLong/int/Double:求平均
List<String> list = Arrays.asList("jack", "bob", "alice", "mark");
double avg = list.stream().collect(Collectors.averagingLong(String::length));
System.out.println(avg);//4.0
SummarizingDouble/Long/Int
为stream中的元素生成了统计信息,返回的结果是一个统计类
public static void main(String[] args) {
List<String> list = Arrays.asList("jack", "bob", "alice", "mark");
IntSummaryStatistics sum = list.stream().collect(Collectors.summarizingInt(String::length));
System.out.println(sum);
//打印结果:IntSummaryStatistics{count=4, sum=16, min=3, average=4.000000, max=5}
}
3.3 groupingBy:
对流中的内元素进行分组
- groupingBy(Function) – 单纯分key存放成Map,默认返回HashMap
- groupingBy(Function, Collector) - 分key后,对每个key的元素进行后续collect操作
- groupingBy(Function, Suppiler, Collector) - 同上,允许通过Suppiler传入其他Map类型
public class Student {
public Student(String name, Integer score) {
this.name = name;
this.score = score;
}
private String name;
private Integer score;
public String getName() {
return name;
}
public Integer getScore() {
return score;
}
public static void main(String[] args) {
List<Student> students = Arrays.asList(new Student("zhangsan", 70),
new Student("lisi", 60),
new Student("wangwu", 70),
new Student("mazi", 60),
new Student("qiqi", 50));
Map<Integer, List<Student>> collect = students.stream()
.collect(Collectors.groupingBy(Student::getScore));
System.out.println(collect);
/**
* 打印结果
* {50=[com.zhaoyu.javabase.Student@4edde6e5],
* 70=[com.zhaoyu.javabase.Student@70177ecd, com.zhaoyu.javabase.Student@1e80bfe8],
* 60=[com.zhaoyu.javabase.Student@66a29884, com.zhaoyu.javabase.Student@4769b07b]}
*/
}
}
downstream操作:可以嵌套多层Collector,如下,分组完之后还可以传入Collector无限操作
public static void main(String[] args) {
List<Student> students = Arrays.asList(new Student("zhangsan", 70),
new Student("lisi", 60),
new Student("wangwu", 70),
new Student("mazi", 60),
new Student("qiqi", 50));
Map<Integer, Map<Integer, List<Student>>> collect = students.stream()
.collect(Collectors.groupingBy(Student::getScore,
Collectors.groupingBy(s -> s.getName().length())));
System.out.println(collect);
//分组之后还可以分组
}
3.4 partitioningBy
partitioningBy:按条件分成true和false两部分
public static void main(String[] args) {
List<Student> students = Arrays.asList(new Student("zhangsan", 70),
new Student("lisi", 60),
new Student("wangwu", 70),
new Student("mazi", 60),
new Student("qiqi", 50));
Map<Boolean, List<Student>> collect = students.stream()
.collect(Collectors.partitioningBy(s -> {
return s.getScore() > 60;
}));
System.out.println(collect);
/**
* {false=[com.zhaoyu.javabase.Student@70177ecd, com.zhaoyu.javabase.Student@1e80bfe8,
* com.zhaoyu.javabase.Student@66a29884],
* true=[com.zhaoyu.javabase.Student@4769b07b, com.zhaoyu.javabase.Student@cc34f4d]}
*/
}
3.5 mapping/reducing
mapping的功能上和stream.map差不多,但是这个支持downstream,也就是支持映射之后,做进一步操作
public static void main(String[] args) {
List<Student> students = Arrays.asList(new Student("zhangsan", 70),
new Student("lisi", 60),
new Student("wangwu", 70),
new Student("mazi", 60),
new Student("qiqi", 50));
Map<Integer, List<String>> collect = students.stream()
.collect(Collectors.mapping(Student::getName, Collectors.groupingBy(String::length)));
System.out.println(collect);
//打印结果:{4=[lisi, mazi, qiqi], 6=[wangwu], 8=[zhangsan]}
}
reducing:对结果进行归集操作
Student student = students.stream().collect(Collectors.reducing((acc, cur) -> {
if (acc.getScore() > cur.getScore()) {
return acc;
}
return cur;
})).get();
4. Function composition 函数组合
函数组合就是把多个函数组合成一个函数来执行
Function<Integer,Integer> f1 = a -> a + 1;
Function<Integer,Integer> f2 = a -> a * 10;
andThen:
先执行andThen前面的函数,再执行后面的函数,相当于数学表达式:f1.andThen(f2) = f2(f1());
Function<Integer,Integer> fn = f1.andThen(f2);
Integer n = fn.apply(10);//先执行f1:10 + 1 = 11,再执行f2:11 * 10 = 110
System.out.println(n);//110
compose
先执行后面的函数,再执行前面的函数,相当于数学表达式:f1.compose(f2) = f1(f2());
Function<Integer,Integer> fc = f1.compose(f2);
Integer c = fc.apply(10);//先执行f2:10 * 10 = 100,再执行f1:100 = 1 = 101
System.out.println(c);//101
5. Optional
Optional是java8引入的一个用于解决空指针异常的类,相当于用Optional把对象包装起来,每次都只针对Optional进行操作,这样就能避免空指针异常的出现;
举个栗子:
没有Optional之前我们要一层一层判空
@Data
public class Person {
private Pet pet;
@Data
public static class Pet {
private String petName;
}
public static void main(String[] args) {
System.out.println(getPetName(null));
}
public static String getPetName(Person person) {
if (person != null) {
Pet pet = person.getPet();
if (pet != null) {
String petName = pet.getPetName();
return petName;
}
}
return "defaultName";
}
}
有了Optional之后,中间任意一层都可以为空
public static void main(String[] args) {
System.out.println(getPetName(null));//打印结果defaultName
}
public static String getPetName(Person person) {
String petName = Optional.ofNullable(person).map(Person::getPet)
.map(Pet::getPetName).orElse("defaultName");
return petName;
}
5.1 创建Optional
empty():
创建一个空的Optional对象,看源码
private static final Optional<?> EMPTY = new Optional<>();
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
Optional.of():
不允许传入null对象,否则会报空指针,应该明确对象不为null的时候才使用
Student stu = new Student("zhangsan", 70);
Optional.of(stu);
Optional.ofNullable()
允许传入null对象
Student stu = new Student("zhangsan", 70);
Optional.ofNullable(stu);
ofNullable源码可以看到如果传入null,会自动创建一个空的Optional对象
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
5.2 get()
可以取回对象实际值,值为null 的时候抛出异常,所以建议配合下面几个api一起用
5.3 orElse (T)
if x!= null return x else return T:如果实际值不为空,则返回实际值,否则返回orElse()方法中给定的值
Student stu = new Student("zhangsan", 70);
String name = Optional.ofNullable(stu.getName()).orElse("lisi");//如果stu.getName()则返回lisi
5.4 orElseGet(Supplier fn)
if x!=null return x else return fn:如果值不为空则返回实际值,否则返回函数fn的执行结果
Student stu = new Student("zhangsan", 70);
String name = Optional.ofNullable(stu.getName()).orElseGet(stu::getNickName);
如果实际值为空,则orElse和orElseGet效果差不多,但是如果实际值不为空,虽然都能获取到实际值,但是orElse中的调用还是会执行,而orElseGet的不会执行,所以建议用orElseGet,减少不必要的代码执行,提升性能
public static void main(String[] args) {
Student stu = new Student("zhangsan", 70);
//虽然stu不为空,但是createStu还是会被执行
Student stu1 = Optional.of(stu).orElse(createStu("lisi", 50));
//stu不为空,createStu不会被执行
Student stu2 = Optional.of(stu).orElseGet(() -> createStu("wangwu", 50));
}
public static Student createStu(String name, Integer score) {
System.out.println(name);
return new Student(name, score);
}
5.5 ifPresent(fn) /isPresent
if x!= null fn:如果实际值不为空,则执行fn函数
Optional.of(stu).ifPresent(s -> System.out.println(s.name));;//如果stu不为空,则输出姓名
isPresent():判断实际值是否为空
boolean present = Optional.of(stu).isPresent();//返回一个布尔值
6. map/flatMap
6.1 map
跟stream的map效果差不多也是可以对内容做映射操作
String petName = Optional.ofNullable(person).map(Person::getPet)
.map(Pet::getPetName).orElse("defaultName");
6.2 flatMap
如果一个对象某个成员属性本身是Optional类型的,可能会出现Optional嵌套情况
@Data
public class Person {
private Pet pet;
//这里不知道pet是否会为空,所以用Optional包装一下,避免空指针出现
public Optional<Pet> getPetOp() {
return Optional.ofNullable(pet);
}
@Data
public static class Pet {
private String petName;
}
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.getPetName(null));
}
public String getPetName(Person person) {
//出现了Optional嵌套情况
Optional<Optional<Pet>> pet = Optional.ofNullable(person).map(Person::getPetOp);
return null;
}
}
如果想获取到真实的Pet,需要用flatMap解掉一层Optional
public String getPetName(Person person) {
Optional<Pet> petOptional = Optional.ofNullable(person).flatMap(Person::getPetOp);
String petName = petOptional.map(Pet::getPetName).orElse("defaultName");
return petName;
}
7. CompletableFuture
Future接口可以构建异步应用,但依然有其局限性。它很难直接表述多个Future 结果之间的依赖性,CompletableFuture正好可以满足这个要求,如何对两个或多个异步操作进行流水线和合并操作;
由于api非常多,可以通过以下方式来理解api的作用
- accept: 接受参数是 Consumer
- apply: 接受参数是 Function
- handle:接受参数是BiFunction
- runAfter : 接受参数是Runnable
- Either/Both: 任一任务完成还是都完成
- then: 等当前任务完成再执行另一个
- async 后续任务是否异步执行
举个栗子
假如需要完成以下的几个异步调用:
service2依赖于service1的执行结果,service4依赖于service2和service3的执行结果
用传统的Future实现方式:
public class FutureExample {
private static ExecutorService pool = Executors.newFixedThreadPool(16);
private static String service1() {
try {
Thread.sleep(1000 * 3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "service 1 result";
}
private static String service2(String retFromService1) {
try {
Thread.sleep(1000 * 3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return retFromService1 + " -> service2";
}
private static String service3() {
try {
Thread.sleep(1000 * 3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "service 3 result";
}
private static String service4(String service3Ret, String service2Ret) {
return String.format("svr3:%s, svr2:%s", service3Ret, service2Ret);
}
public static String mainService() throws InterruptedException, ExecutionException {
//service1和service3没依赖关系,可以分开执行
Future<String> task1 = pool.submit(FutureExample::service1);
Future<String> task3 = pool.submit(FutureExample::service3);
String ret = task1.get();//service2依赖于service1的执行结果
Future<String> task2 = pool.submit(()->service2(ret));
//拿到service2和service3的执行结果
String ret3 = task3.get();
String ret2 = task2.get();
return service4(ret3, ret2);//执行service4
}
}
CompletableFuture实现方式:
public static String mainServiceWithNewAPI() throws InterruptedException, ExecutionException {
//异步执行完service1,再执行service2
CompletableFuture<String> cf2 = CompletableFuture
.supplyAsync(FutureExample::service1, pool)
.thenApply(FutureExample::service2);
//执行service3
CompletableFuture<String> cf3 = CompletableFuture
.supplyAsync(FutureExample::service3, pool);
//cf3和cf2一起执行完后,把结果都传给service4
CompletableFuture<String> cf4 = cf3.thenCombine(cf2, FutureExample::service4);
return cf4.join();
}

浙公网安备 33010602011771号