Java8语言特性
函数式接口
- 函数式接口可以使用lambda表达式,方法引用或者构造函数引用创建实例。
Function
- function是函数式接口中的一个,其主要功能式接受一个参数,有一个返回值,入参和返回类型可以不一样。如下:
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
2. function的应用场景在于能有一个返回值,返回值得类型可以和参数不一致。所以我们可以将其用在相似业务逻辑处理中。只需要将不同的业务逻辑作为函数传入,在公共逻辑中调用apply()来执行不同的部分业务处理即可。如下:
private static void doSomething(java.util.function.Function<Integer, String> fun){
int num = 100;
final String apply = fun.apply(num);
System.out.println("result = " + apply);
}
public static void main(String[] args) {
doSomething(arg -> String.valueOf(arg));
doSomething(arg -> "1" + arg);
}
jdk8使用:
hashmap中computeIfabsent(k, function<k, v>)//如果制定的key不存在,使用函数返回值进行替代
stream中的map //转换流,本质就可以将流中每个对象进行function的处理,最后收集到一个集合
consumer
- consumer接口是消費一個對象,但是没有返回值。
public static void testConsumer() {
//设置好Consumer实现方法
Consumer<Integer> square = x -> System.out.println("平方计算 : " + x * x);
//传入值
square.accept(2);
}
Iterable接口中的forEach()方法就应用了consumer,其参数就是接收一个consumer函数接口
public interface Iterable<T> {
//forEach方法传入的就是Consumer
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
}
supplier
supplier用于提前定义返回的一个执行类型结果,等需要调用的时候再通过调用get()获取结果。
Optional中的orElseGet()方法中有用到。
// supperlier定义
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
//使用示例
public static void main(String[] args) {
Person son = null;
//先判断son是否为null,如果为不为null则返回当前对象,如果为null则返回新创建的对象
BrandDTO optional = Optional.ofNullable(son).orElseGet(() -> new Person());
}
应用场景:解决分支结构的频繁,提取一个函数用于判定分支的执行,对于不满足条件所走的逻辑就是我们要传入提取函数的自定义的逻辑,也就是要接收的supplier类型参数。
predicate
predicate接口主要用来判定一个参数是否符合要求。比如在stream中的filter()需要传入一个predicate接口函数。
List<User> collect = list.stream().filter(x -> x.getAge() > 2).collect(Collectors.toList());
System.out.println("获取年龄大于2的数量 = " + collect.size());
Stream
Stream是一种可供流式操作的数据视图,它不改变源数据集合,如果改变就会返回一个新的数据集合。
- stream不存储数据
- 不改变源数据
- 可以延迟执行
stream操作:
-
中间操作:仅仅起到标记作用,只有结束操作才会触发实际计算。
- filter:参数是predicate函数接口,用于判断是否满足条件
- map:转换流,参数是一个function函数接口,将一种类型的流转换为另一种类型
- distinct: 去重流
- sorted:返回要给去重的流
- limit:返回前n个元素组成的流
- skip:返回第n个元素后面数据组成的流
-
结束操作:触发实际计算
- forEach: 遍历流中数据
- toArray:返回元素对应的数组对象
- reduce:聚合操作,用作统计
- collect:聚合并封装程目标数据
举例:
// 将list封装为map
Map<Integer, String> namemap = persionList.stream().collect(Collectors.toMap(Person::getId, Person::getName));
System.out.println(namemap.toString());
stream中的几个常用操作
扁平化flatmap
从效果上来看,flatmap实现的是降维打击,可以将二维转化为一维。但它其实本身具有的功能是将多个流合并为一个流。从这个方法的参数和返回值来看: Stream flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);,其参数function要返回的是float map参数类型的流,整个float map返回的也是这种类型的流。说明float map是一种无状态的中间操作,只是负责将每个元素流化操作返回的stream合并到一起。最终实现“降维打击”。
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
final List<Character> collect = list.stream().flatMap((e) -> filterCharacter(e)).collect(Collectors.toList()); // filterCharacter:转变为字符流
\boxd()
boxd是实现的装箱操作,主要是将基本数据类型的流转换为对应的引用类型包装类类型。对于后续的某些操作必须要这么做,比如collectors.toList()这个操作,就必须先装箱,因为集合类不能存放基本数据类型
random.ints(0, 100).limit(10).boxed().collect(Collectors.toList());
stream中的装箱操作
在进行stream中的一些归约操作的时候,我们可以将普通流转换为对应的数值流,因为归约操作返回值基本都是基本数据类型或者其对应的optional类型,这样做可以减少装箱或者拆箱的成本。
int sum = Arrays.asList(1,2,3,4).stream().mapToInt(i -> i).sum();
Stream的性能分析
测试案例:xie.infoq.cn/article/652…
总结:在无特殊业务需求的情况下,尽可能多的使用stream流去取代迭代器遍历。少量的数据量场景下,涉及到多步操作就适用stream流,大量数据场景下,不要使用迭代器遍历,如果机器是多核CPU可以考虑使用parallel stream来进一步提高效率。
Optional
optional是j8为了npe添加的一个功能类,它可以避免显示的进行null值判断,使代码开发更加的简洁,优雅。
optional对象完全使用静态工厂方法创建,包括of(); ofNullable(); empty().并且允许创建一个空值的optional对象
get,isPresent
get方法返回包装类中的值,不存在则会抛出异常;isPresent()返回是否为空,实际运用中非必要不要使用这两个方法。其破坏了optional存在的必要性。
ifPresent
这个方法的参数是consumer函数接口,包装值存在的时候就会执行这个consumer,大部分情况都用这个方法替代isPresent方法。
Optional.ofNullable(student).ifPresent(u -> System.out.println("The student is : " + u.getName()));
filter
filter主要是过滤,接收的是一个predicate接口,当满足条件的时候返回的是返回本身,否则就返回一个空的optional对象。通常后面会跟一个ifPresent方法来做后续操作,这个方法也是用来取代get后判断的操作的。
Optional.ofNullable(student).filter( u -> u.getAge() > 18).ifPresent(u -> System.out.println("The student age is more than 18."));
map
map和stream中的map方法一样起到转换的作用,可以将一种类型的optional转换为另一种类型。接收的也是一个function接口
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));
}
}
-------
public static Optional<Integer> getAge(Student student){
return Optional.ofNullable(student).map(u -> u.getAge());
}
floatMap
它和map的区别在于,接收的function接口的返回值变成了optional类型,也就是说,其本身如果运用在将一种类型转换为另一个种类型上没有任何区别。它特别的应用场景应该是将二维的optional类型转换为一维optional类型。
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
从源码上看,两种方法其最终返回值都是一样,所不同的是,floatmap要求function返回的optional必须不能是null。二维的场景下,也就是要求其内部的optional不能是null。
orElse,orElseGet
两者都是判断包装值是否为空,为空则执行参数处理。不同的是前者返回参数指定值,后者是执行一段逻辑,这段逻辑是一个supplier接口。这两者和ifPresent都可以作为一个相反逻辑兜底。还有一个orelseThrow(),不同的是它要抛出异常,非特殊场景,我们一般不要抛出异常,否则就违背了optional的设计初衷。
异步计算
Future
提供一种异步并行计算的功能,也是使用callable创建一个多线程的方式
FutureTask<UserInfo> userInfoFutureTask = new FutureTask<>(new Callable<UserInfo>() {
@Override
public UserInfo call() throws Exception {
return userInfoService.getUserInfo(userId);
}
});
executorService.submit(userInfoFutureTask);
UserInfo userInfo = userInfoFutureTask.get();
这种方式虽然能起到一个异步计算的效果,但是对于获取结果并不友好,只能通过轮询(idDone方法),阻塞(get方法)的方式得到任务结果。这就违背了异步编程的思想
completableFuture(示例见源码)
completableFuture是Java实现的一个进行异步计算对象,并且提供了一种类似观察者的机制,在任务执行完成后通知监听的一方。
- 创建异步任务
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
2. 简单异步回调方法
- 多任务组合处理