一、Lambda表达式
Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。
1. 作用
简化代码
2. 在java中的应用
List<String> list = Arrays.asList("A", "B", "C", "D");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
for (String str : list) {
System.out.println(str);
}
list.forEach(num -> System.out.println(num));
list.forEach(System.out::println);
@FunctionalInterface
public interface FunctionInterfaceTest{
int calculator(int left , int right);
}
//不带参数的lambda使用
private static int calculator(FunctionInterfaceTest functionAPI){
return functionAPI.calculator(100, 2);
}
//原生写法
@Test
public void testFunctionInterface(){
int result = calculator(new FunctionInterfaceTest() {
@Override
public int calculator(int a, int b) {
return a * b;
}
});
System.out.println(result);
}
//使用lambad表达式
@Test
public void testFunctionInterface(){
int result = calculator((a, b) -> a * b);
System.out.println(result);
}
//带参数的lambda使用
private static int calculator(FunctionInterfaceTest functionAPI, int param1, int param2) {
return functionAPI.calculator(param1, param2);
}
@Test
public void testFunctionInterfaceWitchParam() {
int param1 = 100;
int param2 = 2;
int result = calculator((a, b) -> a * b, param1, param2);
System.out.println(result);
}
3. 方法引用
静态方法引用
例如Integer的parseInt方法
List<String> list = Arrays.asList("1", "2", "3", "4");
list.forEach(num -> Integer.parseInt(num));
list.forEach(Integer::parseInt);
实例方法的引用
例如StringBuilder的append方法
StringBuilder sbd = new StringBuilder();
list.forEach(str -> sbd.append(str));
list.forEach(sbd :: append);
二、Stream流
1. 什么是Stream流
从支持数据处理操作的源生成的元素序列
@Test
public void createStream(){
List<String> list = new ArrayList<>();
list.add("Tom");
list.add("Jerry");
//调用了该方法的类就可以使用流 StreamSupport.stream(spliterator(), false);
Stream<String> streamA = list.stream();
Integer[] arrInt = new Integer[]{111,222};
Stream<Integer> streamB = Stream.of(arrInt);
}
2. 分类
Stream 串行流、parallelStream 并行流
ps:并行流并不是一个独立的接口,实际上也是Stream,只不过是支持了并行操作
3. 作用
- 让你实现更简洁易读、灵活、性能更好的代码
- 提供函数操作数据
- 提供多线程,更高效地处理集合
- 并行流内部已经实现了多线程和锁,不用关心其内部实现
4. 流的结构
数据源 -> 创建流 -> 中间操作 -> 终端操作
5. 创建流
- 由值创建流
Integer[] arrInt = new Integer[]{111,222};
Stream<Integer> streamB = Stream.of(arrInt);
- 由数组创建流
int[ ] arr = { 5,4,4,20};
IntStream intStream = Arrays.stream(arr);
- 由文件创建流
Stream<String> fileStream = Files.lines(Paths.get("E:\\workspace\\streamReadFileTest.txt"), Charset.defaultCharset());
fileStream.flatMap((Function<String, Stream<String>>) s -> Arrays.stream(s.split(" "))).distinct().forEach(System.out::println);
- 由函数生成流,创建无限流
Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);
Stream.generate(Math::random).limit(5).forEach(System.out::println);
IntSupplier intSupplier = new IntSupplier() {
private int previous = 0;
private int current = 1;
public int getAsInt() {
int oldPrevious = this.previous;
int nextValue = this.previous + this.current;
this.previous = this.current;
this.current = nextValue;
return oldPrevious;
}
};
IntStream.generate(intSupplier).limit(10).forEach(System.out::println);
6. 中间操作
- 操作流,返回结果也是一个流
- 在没有调用终端操作前,不会运行,类似只是声明一个方法体
6.1 filter
过滤满足条件的元素
Integer[] arrInt = new Integer[]{111, 222};
int result = Stream.of(arrInt).filter(num -> num > 200).mapToInt(value -> value).sum();
System.out.println(result);
6.2 map
将流转换为另一个流
//userList的ID取出来重新获取
List<UserInfo> userList = new ArrayList<>();
userList.add(new UserInfo(100L, "Tom"));
userList.add(new UserInfo(200L, "Jerry"));
List<Long> idList = new ArrayList<>(userList.size());
for (UserInfo userInfo : userList){
idList.add(userInfo.getId());
}
List<Long> ids = userList.stream().map(UserInfo::getId).collect(Collectors.toList());
System.out.println(ids);
6.3 flatmap
将流中的每个元素转换成一个单独的流,再合并成一个流
//将数组里的字母以“,”分割输出
String[] arr = {"A,B,C", "D,E,F", "G,H"};
Arrays.stream(arr).map(str -> str.split(",")).forEach(System.out::println);
Arrays.stream(arr).flatMap(childArr -> Arrays.stream(childArr.split(","))).forEach(System.out::println);
6.4 reduce
规约、推演
将流的第一个数作为参数,执行函数操作
Integer result = Stream.of(1, 2, 3, 4).reduce((integer, integer2) -> integer * integer2).get();
System.out.println(result);
6.5 sorted
排序
//将用户按ID排序
List<UserInfo> userList = Stream.of(156, 324, 476, 78, 79, 23, 5, 43, 1, 6786, 22234).
map(num -> new UserInfo(Long.valueOf(num), num.toString())).collect(Collectors.toList());
userList.stream().sorted((user1, user2) -> (int) (user1.getId() - user2.getId())).forEach(System.out::println);
Stream.of(156, 324, 476, 78, 79, 23, 5, 43, 1, 6786, 22234).sorted().forEach(System.out::println);
6.6 limit
截断流
和常规的List、数组相比,不用关心下标是否越界
//排序取最小3个数
int[] arr = {156, 324, 476, 78, 79, 23, 5, 43, 1, 6786, 22234};
Arrays.stream(arr).sorted().limit(3).forEach(System.out::println);
6.7 skip
跳过前N个元素,返回剩下的元素,和limit方法互补
//排序取最大3个数
int[] arr = {156, 324, 476, 78, 79, 23, 5, 43, 1, 6786, 22234};
Arrays.stream(arr).sorted().skip(arr.length - 3).forEach(System.out::println);
6.8 distinct
去重
int[] arr = {1, 1, 23, 5, 43, 1, 6786, 22234};
Arrays.stream(arr).distinct().sorted().forEach(System.out::println);
7. 终端操作
中间操作相当于是声明方法、设计处理流程,但是不会真正执行方法,只有当终端操作被调用的时候,才会执行前面的操作。
7.1 collect
合并汇总结果,转换为特定的集合
//数组转换为List
int[] arr = {156, 324, 476, 78, 79, 23, 5, 43, 1, 6786, 22234};
List<Integer> list = Arrays.stream(arr).collect(ArrayList::new, List::add, List::addAll);
List<Integer> list = Arrays.stream(arr).collect(new Supplier<List<Integer>>() {
//初始化
@Override
public List<Integer> get() {
return new ArrayList<>(10);
}
}, new ObjIntConsumer<List<Integer>>() {
//循环接收值
@Override
public void accept(List<Integer> integers, int value) {
integers.add(value);
}
}, new BiConsumer<List<Integer>, List<Integer>>() {
//批量接收
@Override
public void accept(List<Integer> integers, List<Integer> integers2) {
integers.addAll(integers2);
}
});
//Collectors工具类转换
int[] arr = {156, 324, 476, 78, 79, 23, 5, 43, 1, 6786, 22234};
List<Integer> list3 = Stream.of(arr).flatMap(ints -> Arrays.stream(ints).boxed()).collect(Collectors.toList());
//数组作为转换为实体的Map集合
int[] arr = {156, 324, 476, 78, 79, 23, 5, 43, 1, 6786, 22234};
Map<Long, UserInfo> userMap = Stream.of(arr).flatMap(ints -> Arrays.stream(ints).boxed())
.map(num -> new UserInfo(Long.valueOf(num), String.valueOf(num)))
.collect(Collectors.toMap(UserInfo::getId , userInfo -> userInfo));
7.2 forEach
执行lambda循环,无返回值
int[] arr = {156, 324, 476, 78, 79, 23, 5, 43, 1, 6786, 22234};
Arrays.stream(arr).forEach(System.out::println);
7.3 count
返回流中元素的个数,long型
int[] arr = {156, 324, 476, 78, 79, 23, 5, 43, 1, 6786, 22234};
long result = Arrays.stream(arr).count();
System.out.println(result);
7.4 match
短路匹配,不需要处理整个流,找到结果就返回boolean
int[] arr = {156, 324, 476, 78, 79, 23, 5, 43, 1, 6786, 22234};
boolean anyMatch = Arrays.stream(arr).anyMatch(num -> num == 5);
System.out.println(anyMatch);
boolean allMatch = Arrays.stream(arr).allMatch(num -> num == 156);
System.out.println(allMatch);
boolean noneMatch = Arrays.stream(arr).noneMatch(num -> num < 0);
System.out.println(noneMatch);
7.5 find
查找
//findFirst
int[] arr = {156, 324, 476, 78, 79, 23, 5, 43, 1, 6786, 22234};
int first = Arrays.stream(arr).findFirst().getAsInt();
System.out.println(first);
int[] emptyArr = {};
// int error = Arrays.stream(emptyArr).findFirst().getAsInt();//NoSuchElementException
int orElseGet = Arrays.stream(emptyArr).findFirst().orElseGet(new IntSupplier() {
@Override
public int getAsInt(){
return -1;
}
});
System.out.println(orElseGet);
int orElse = Arrays.stream(emptyArr).findFirst().orElse(-1);
System.out.println(orElse);
//finAny
int[] arr = {156, 324, 476, 78, 79, 23, 5, 43, 1, 6786, 22234};
int findAny = Arrays.stream(arr).findAny().getAsInt();
System.out.println(findAny);
Arrays.stream(arr).findAny().ifPresent(value -> System.out.println(value + 1000000));
7.6 max
求最大值
int[] arr = {156, 324, 476, 78, 79, 23, 5, 43, 1, 6786, 22234};
int max = Arrays.stream(arr).max().getAsInt();
System.out.println(max);
8. Optional
选择容器
8.1 作用
Stream流部分终端操作接收值的容器
提供防止空指针异常发生的方法
//在不使用Optional的情况下,接口处理
private void doSomeThing(UserInfo user){
if(null != user){
Long userId = user.getId();
if(null != userId){
//doSomeThing.....
}
}
//or
if(null == user){
return;
}
Long userId = user.getId();
if(null == userId){
return;
}
//doSomeThing.....
System.out.println("userId: " + userId);
}
8.2 of / ofNullable
创建Optional对象
//使用Optional的接口处理
public void doSomeThingWithOptional(UserInfo user){
Optional.ofNullable(user).map(UserInfo::getId).ifPresent(userId -> System.out.println("userId: " + userId));
}
UserInfo userInfo = new UserInfo(1234L, "tom");
//创建空Optional对象
Optional<UserInfo> emptyOptional = Optional.empty();
//创建非空Optional对象
Optional<UserInfo> userOptional = Optional.of(userInfo);
//创建Optional对象,如果是空,则会报空指针
Optional<UserInfo> nullUser = Optional.of(null);
//创建Optional对象,如果是空,也不会报错
Optional<UserInfo> ofNullableUser = Optional.ofNullable(null);
8.2 get / orElse / orElseGet
get 获取对象中的值,如果对象为空 ,会抛出空指针异常
orElse 获取对象中的值,如果对象为空 ,可以定义一个默认值返回,需要和原对象类型相同
orElseGet 获取对象中的值,如果对象为空 ,执行Supplier方法返回一个默认值,需要和原对象类型相同
UserInfo userInfo = new UserInfo(1234L, "tom");
Optional<UserInfo> userOptional = Optional.ofNullable(userInfo);
System.out.println(userOptional.get());
userInfo = null;
userOptional = Optional.ofNullable(userInfo);
// System.out.println(userOptional.get());//NoSuchElementException
System.out.println(userOptional.orElse(new UserInfo(1000L, "我是默认用户")));
System.out.println(
userOptional.orElseGet(() -> {
System.out.println("do something...");
return new UserInfo(2000L, "我是默认用户");
})
);
8.3 ifPresent / isPresent
ifPrensent 当值存在,执行Consumer方法
isPresent 当值存在,返回true
List<String> list= Arrays.asList("tom", "jerry");
Optional.of(list).filter(str -> str.contains("tom")).ifPresent(System.out::println);
boolean isPresent = Optional.of(list).filter(str -> str.contains("tom")).isPresent();
System.out.println(isPresent);
8.4 map / flatMap / filter
和Stream的功能一样,只是返回的对象不同,这里返回的是Optional对象
//用name列表生成UserInfo列表
List<String> list = Arrays.asList("tom", "jerry");
List<UserInfo> userList = Optional.of(list).map(list1 -> {
AtomicLong id = new AtomicLong(0L);
return list1.stream().map(name -> {
id.getAndIncrement();
return new UserInfo(id.get(), name);
}).collect(Collectors.toList());
}).orElse(Collections.emptyList());
System.out.println(userList);
9. Collector接口
String[] arr = new String[]{"1","3","4","65"};
//Collectors工具类生成Collector接口实现
Arrays.stream(arr).collect(Collectors.toList());
Collector<T, A, R>
T:入参的类型
A:可变累积规约操作的返回参数类型
R:返回结果的类型
接口方法
9.1 supplier
public <A> supplier() 创建一个返回结果的容器
//创建容器
@Override
public Supplier<List<String>> supplier() {
return ArrayList::new;
}
9.2 accumulator
BiConsumer<A, T> accumulator()
将一个T类型的参数,加到A类型的容器中
//消费
@Override
public BiConsumer<List<String>, String> accumulator() {
return new BiConsumer<List<String>, String>() {
@Override
public void accept(List<String> list, String str) {
list.add(str);
list.add(str);
}
};
}
9.3 combiner
BinaryOperator<A> combiner()
将两部分结果合并,返回原结果容器或者新结果容器
//合并子结果容器
@Override
public BinaryOperator<List<String>> combiner() {
return new BinaryOperator<List<String>>() {
@Override
public List<String> apply(List<String> list1, List<String> list2) {
list1.addAll(list2);
return list1;
}
};
}
9.4 characteristics
Set<Collector.Characteristics> characteristics()
返回一个集合特征设置
CONCURRENT :表明收集器是一个并行的,accumulator方法会被多个线程调用,累积在相同的容器
UNORDERED:无序的,表明容器的最终顺序,不一定和入参的顺序一致
IDENTITY_FINISH:表明最终结果和原类型一致,finisher不会被调用
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(Characteristics.CONCURRENT));
}
9.5 finisher
Function<A,R> finisher()
将accumulator累加的结果,转换为新结果容器。如果characteristics设置了IDENTITY_TRANSFORM,会直接返回原结果类型。
//对结果容器应用最终转换
@Override
public Function<List<String>, List<String>> finisher() {
return new Function<List<String>, List<String>>() {
@Override
public List<String> apply(List<String> list) {
// 去重
return new ArrayList<>(new HashSet<>(list));
}
};
}
9.6 of
static <T,A,R> Collector<T,A,R> of(Supplier<A> supplier, BiConsumer<A,T> accumulator, BinaryOperator<A> combiner, Function<A,R> finisher, Collector.Characteristics... characteristics)
返回一个Collector实例,包含实现接口的方法参数
10. Collectors
收集容器工具类,实现了各种有用的规约操作,比如累加、求和、求最大最小值、分组 、求平均值等
10.1 counting
统计数据量
long counting = Stream.of(123, 324, 3, 56, 10000).collect(Collectors.counting());
10.2 groupingBy
分组
List<UserInfo> userList = new ArrayList<>();
userList.add(new UserInfo(100L, "tom"));
userList.add(new UserInfo(200L, "tom"));
userList.add(new UserInfo(300L, "jerry"));
userList.add(new UserInfo(400L, "jerry"));
Map<String, List<UserInfo>> groupMap = userList.stream().collect(Collectors.groupingBy(UserInfo::getName));
//多级分组
Map<String, Map<Long, List<UserInfo>>> multiGroupMap = userList.stream().collect(
Collectors.groupingBy(
UserInfo::getName, Collectors.groupingBy(UserInfo::getId)
)
);
10.3 maxBy / minBy
求最大最小值
List<UserInfo> userList = new ArrayList<>();
userList.add(new UserInfo(100L, "tom"));
userList.add(new UserInfo(200L, "tom"));
userList.add(new UserInfo(300L, "jerry"));
userList.add(new UserInfo(400L, "jerry"));
UserInfo maxUser = userList.stream().collect(Collectors.maxBy((o1, o2) -> (int) (o1.getId() - o2.getId()))).orElse(null);
UserInfo minUser = userList.stream().collect(Collectors.minBy((o1, o2) -> (int) (o1.getId() - o2.getId()))).orElse(null);
10.4 summingInt
汇总求和
long sum = Stream.of(123, 324, 3, 56, 10000).collect(Collectors.summingInt(value -> value));
10.5 averagingInt
求平均数
double average = Stream.of(123, 324, 3, 56, 10000).collect(Collectors.averagingInt(value -> value));
10.6 reducing
广义规约汇总
//1.有初始值 2.直接返回值
Integer result = Stream.of(100, 1, 3, 56, 300).collect(Collectors.reducing(1, (a, b) -> a + b));
System.out.println(result);
//1.无初始值 2.返回的是Optional对象
result = Stream.of(100, 1, 3, 56, 300).collect(Collectors.reducing((a, b) -> a + b)).orElse(-1);
System.out.println(result);
//1.有初始值 2.有转换方法 3.直接返回值
result = Stream.of("100", "1", "3", "56", "300").collect(Collectors.reducing(1, num -> Integer.valueOf(num), (a, b) -> a + b));
System.out.println(result);
10.7 collectingAndThen
收集,并对其结果应用转换函数
int result = Stream.of("100", "1", "3", "56", "300").collect(Collectors.collectingAndThen(Collectors.toList(), list -> list.stream().mapToInt(Integer::valueOf).sum()));
10.8 partitionBy
分区,返回true/false的分组
分区还可以实现二级分组,第二个参数传groupingBy
Map<Boolean, List<Integer>> result = Stream.of("100", "-1", "3", "-56", "-300").map(Integer::valueOf).collect(Collectors.partitioningBy(integer -> integer >0));
三、并行流
Stream流的一种,可以实现并行处理数据流
1. 创建并行流
流对象调用parallel
//list创建并行流
List<String> list = new ArrayList<>();
list.add("Tom");
list.add("Jerry");
//调用了该方法的类就可以使用流 StreamSupport.stream(spliterator(), false);
Stream<String> streamA = list.parallelStream();
//由值创建并行流
Integer[] arrInt = new Integer[]{111, 222};
Stream<Integer> streamB = Stream.of(arrInt).parallel();
//由数组创建并行流
int[] arr = {5, 4, 4, 20};
IntStream intStream = Arrays.stream(arr).parallel();
//由文件创建并行流
try {
Stream<String> fileStream = Files.lines(Paths.get("E:\\workspace\\streamReadFileTest.txt"), Charset.defaultCharset());
fileStream.parallel().flatMap((Function<String, Stream<String>>) s -> Arrays.stream(s.split(" "))).distinct().forEach(System.out::println);
} catch (IOException e) {
throw new RuntimeException(e);
}
//由函数生成流,创建无限流
Stream.iterate(0, n -> n + 2).parallel().limit(10).forEach(System.out::println);
2. 使用注意事项
2.1 选择合适的数据结构,比优化算法更为重要
2.2 并行流不一定比顺序流快
- 比如一些依赖顺序的操作findFirst、limit
- 计算的数据量小
- 额外的拆分、合并结果的性能开销大于计算本身
2.3 留意装箱,自动装箱、拆箱会大大降低性能
2.4 测试性能
long start = System.currentTimeMillis();
long result = Stream.iterate(1L, n -> n + 1).limit(100000000).reduce(0L, Long::sum);
long end = System.currentTimeMillis();
System.out.println(result);
System.out.println("耗时:" + (end - start));
int core = Runtime.getRuntime().availableProcessors();
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", String.valueOf(core));
long start2 = System.currentTimeMillis();
long result2 = Stream.iterate(1L, n -> n + 1).limit(100000000).parallel().reduce(0L, Long::sum);
long end2 = System.currentTimeMillis();
System.out.println(result2);
System.out.println("并行耗时:" + (end2 - start2));
//输出结果
5000000050000000
耗时:1032
5000000050000000
并行耗时:21130
long start = System.currentTimeMillis();
long result = LongStream.rangeClosed(1L, 100000000).sum();
long end = System.currentTimeMillis();
System.out.println(result);
System.out.println("耗时:" + (end - start));
long start2 = System.currentTimeMillis();
long result2 = LongStream.rangeClosed(1L, 100000000).parallel().sum();
long end2 = System.currentTimeMillis();
System.out.println(result2);
System.out.println("并行耗时:" + (end2 - start2));
//
5000000050000000
耗时:60
5000000050000000
并行耗时:10
3. 并行流的优势
简化了并行开发的代码,底层实现并行,自动拆分流处理合并结果,帮助我们省略了很多线程并行的问题
//计算1亿的累加和
long start = System.currentTimeMillis();
ExecutorService executor = Executors.newFixedThreadPool(10);
int core = Runtime.getRuntime().availableProcessors();
int remainder = 100000000%core;
//以CPU核心数切分线程处理
int size = 100000000/core;
List<FutureTask<Long>> listFuture = new ArrayList<>(core);
for (int j = 0; j < core; j++) {
int batch = j;
FutureTask<Long> sumFuture = new FutureTask<>(() -> {
long result = 0L;
for (int i = batch *size + 1; i < batch*size + size +1; i++) {
result = result + i;
}
return result;
});
executor.submit(sumFuture);
listFuture.add(sumFuture);
}
//汇总结果
long sum = 0L;
for(FutureTask<Long> sumFuture : listFuture){
if(null != sumFuture.get()){
sum = sum + Long.parseLong(sumFuture.get().toString());
}
}
//处理余数
if(remainder > 0){
for (int i = core*size + 1; i < core*size + remainder; i++) {
sum = sum + i;
}
}
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.println("常规并行线程耗时:" + (end - start));
//用并行流处理
long start2 = System.currentTimeMillis();
long result2 = LongStream.rangeClosed(1L, 100000000).parallel().sum();
long end2 = System.currentTimeMillis();
System.out.println(result2);
System.out.println("并行耗时:" + (end2 - start2));
四、 Fork/Join分支合并框架
1. ForkJoinTask
ForkJoinTask抽象类,是并行流底层用的框架
一个轻量级线程对象,在ForkJoinPool内部运行。以牺牲部分功能使用的代价,以递归方式将可以并行的任务拆分成更小的任务,然后将每个子任务的结果合并起来生成整体结果,ForkJoinPool是它的工作线程池
2. 实现类
- RecursiveTask<T> 返回结果
- RecursiveAction 无返回结果
- CountedCompleter 可以记住目前还在进行中的任务数,并且可以通知onCompletion这个方法
进行中的任务数可以通过调用CountedCompleter#addToPendingCount ()来增加,我们在fork一个task的时候务必调用这个方法,表明进行中的任务+1
3. RecursiveTask应用
public class RecursiveTaskTest extends RecursiveTask<Long> {
private long num;
private long batchSize;
RecursiveTaskTest(long num, long batchSize){
this.num = num;
this.batchSize = batchSize;
}
@Override
protected Long compute() {
if (num > batchSize) {
long childNum = num - batchSize;
RecursiveTaskTest task1 = new RecursiveTaskTest(batchSize, batchSize);
RecursiveTaskTest task2 = new RecursiveTaskTest(childNum, batchSize);
task2.fork();
return task1.compute() + task2.join();
} else {
return getSum(num);
}
}
private long getSum(long num){
long sum = 0L;
for (long i = 0; i < num; i++) {
sum++;
}
return sum;
}
}
ForkJoinPool forkJoinPool = new ForkJoinPool();
RecursiveTaskTest task = new RecursiveTaskTest(10000000000L, 1000000000L);
forkJoinPool.invoke(task);
System.out.println(task.join());
forkJoinPool.shutdown();
Fork方法
用于将新创建的子任务放入当前线程的work queue队列中,Fork/Join框架将根据当前正在并发执行ForkJoinTask任务的ForkJoinWorkerThread线程状态,决定是让这个任务在队列中等待,还是创建一个新的ForkJoinWorkerThread线程运行它,又或者是唤起其它正在等待任务的线程来执行
4. work stealing 工作窃取
切分子任务后,每个CPU会从当前任务的双向链式队列的队头取下一个任务执行,当执行效率不同时,部分CPU提前完成任务,可能会造成部分CPU闲置。为了解决这个问题,work stealing机制会让闲置的CPU从其他的任务的双向链表式队列的队尾取一个任务执行。
5. Spliterator 可拆分迭代器
Java 8已经为集合框架中包含的所有数据结构提供了一个默认的Spliterator实现。集合实现了Spliterator接口,接口提供了一个默认的spliterator()方法。和Iterator一样,Spliterator也用于遍历数据源中的元素,但它是为了并行执行而设计的。
public interface Spliterator<T> {
boolean tryAdvance(Consumer<? super T> action);
Spliterator<T> trySplit();
long estimateSize();
int characteristics();
}
- tryAdvance:迭代元素,类似iterator,有下一个值的话返回true
- trySplit:把元素拆分给另一个Spliterator执行,拆分后继续递归调用trySplit,知道返回null,不需要再拆分为止
- estimateSize:评估拆分的个数
- characteristics:返回一个int,代表Spliterator本身特性集的编码
五、 default方法
修饰接口方法,提供默认处理
作用
新增、修改了接口后,实现了该接口的类不必马上修改,添加新的实现,达到平滑升级的作用
//比如java8对Collection集合新引入的流
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
如果没有default方法,实现了Collection接口的所有原生jdk方法都需要修改,java7的用户直接升级java8也将带来灾难性的后果
六、 新的日期和时间API
1. 为什么需要新的时间日期API
1.1 旧Date类的缺陷
- 表示时间是从1970开始计算,如果要表示2017-09-21,需要这样定义Date date = new Date(117, 8, 21);
- 打印格式不易理解,如果在不使用DateFormat的情况,将打印 Thu Sep 21 00:00:00 CET 2017
- toString方法返回了时区信息
- 月份起始从0开始
1.2 Calendar类的缺陷
- 同样有月份起始从0开始的问题
- 解决了年份从1970开始的问题,但仍不完美
1.3 依赖DateFormat进行格式转换,但是这个类是可变的、有线程安全性问题的,增加了维护成本
2. LocalDate
表示日期信息,不包含时间
创建实例
LocalDate localDate1 = LocalDate.now();
LocalDate localDate2 = LocalDate.of(2017, 9, 21);
LocalDate localDate3 = LocalDate.parse("2017-09-21");
3. LocalTime
表示时间信息
创建实例
LocalTime localTime1 = LocalTime.now();
LocalTime localTime2 = LocalTime.of(13, 45, 20);
LocalTime localTime3 = LocalTime.parse("13:45:20");
4. LocalDateTime
合并了日期和时间,但是没有时区信息
LocalDate localDate = LocalDate.of(2017, 9, 21);
LocalTime localTime = LocalTime.of(13, 45, 20);
//创建DateTime实例 2017-09-21 13:45:20
LocalDateTime dt1 = LocalDateTime.of(2017, Month.SEPTEMBER, 21, 13, 45, 20);
LocalDateTime dt2 = LocalDateTime.of(localDate, localTime);
LocalDateTime dt3 = localDate.atTime(13, 45, 20);
LocalDateTime dt4 = localDate.atTime(localTime);
LocalDateTime dt5 = localTime.atDate(localDate);
LocalDateTime dt = LocalDateTime.of(2014, Month.SEPTEMBER, 21, 13, 45, 20);
LocalDate date = dt.toLocalDate();//转换为日期实例
LocalTime time = dt.toLocalTime();//转换为时间实例
5. Instant
机器的时间表示方式,以1970-1-1开始计算的长整数,支持纳秒精度
Instant instant1 = Instant.now();
Instant instant2 = Instant.parse("2007-12-03T10:15:30.00Z");//dateTime时间字符串转为Instant
Instant instant3 = Instant.ofEpochSecond(3);
Instant instant4 = Instant.ofEpochSecond(3, 0);//3秒后
Instant instant5 = Instant.ofEpochSecond(2, 500_000_000);//2秒之后,再加5亿纳秒
Instant instant6 = Instant.ofEpochSecond(4, -1_000_000_000);//4秒之后,再减10亿纳秒
6. Duration
求时间间隔
创建实例
LocalTime time1 = LocalTime.of(13, 45, 20);
LocalTime time2 = LocalTime.of(14, 45, 20);
LocalDateTime dateTime1 = LocalDateTime.of(2017, Month.SEPTEMBER, 21, 13, 45, 20);
LocalDateTime dateTime2 = LocalDateTime.of(2018, Month.SEPTEMBER, 21, 13, 45, 20);
Instant instant1 = Instant.parse("2007-12-03T10:15:30.00Z");//dateTime时间字符串转为Instant
Instant instant2 = Instant.now();
Duration d1 = Duration.between(time1, time2);
Duration d2 = Duration.between(dateTime1, dateTime2);
Duration d3 = Duration.between(instant1, instant2);
System.out.println(d1);//PT1H
System.out.println(d2);//PT8760H
System.out.println(d3);//PT137572H37M39.393S
System.out.println(d1.toHours());//1
System.out.println(d2.toDays());//365
System.out.println(d3.toDays());//5732
7. Proid
和Duration类似,用于计算时间间隔
区别
- Period - 计算两个“日期”间隔
- Duration - 计算两个“时间”间隔
Period 类与 Duration 类都是一段持续时间的概念,所以就需要一个固定的时间值来配合使用
- Period 对应使用 LocalDate ,它们的作用范围域都是日期(年/月/日)
- Duration 对应使用 Instant、LocalTime、LocalDateTime,它们的作用范围域都是时间(天/时/分/秒/毫秒/纳秒)
创建实例
Period period1 = Period.of(2017, 9, 18);//P2017Y9M18D
LocalDate localDate1 = LocalDate.of(2017, 9, 18);
LocalDate localDate2 = LocalDate.of(2018, 9, 18);
Period period2 = Period.between(localDate1, localDate2);//P1Y
Period period3 = Period.parse("P2017Y9M18D");//P2017Y9M18D
8. ZoneId
新版java.time.ZoneId类是老版java.util.TimeZone类的替代品。它的设计目标就是要让你无须为时区处理的复杂和烦琐而操心,比如处理夏令时(daylight saving time, DST)
创建实例
ZoneId romeZone = ZoneId.of("Europe/Rome");
//使用老版的时区转换为新版的时区ID
ZoneId zoneId = TimeZone.getDefault().toZoneId();//Asia/Shanghai
//将ZoneId和LocalDate、LocalDateTime,构造一个ZonedDateTime实例
LocalDate date = LocalDate.of(2017, Month.MARCH, 18);
ZonedDateTime zdt1 = date.atStartOfDay(romeZone);//2017-03-18T00:00+01:00[Europe/Rome]
LocalDateTime dateTime = LocalDateTime.of(2017, Month.MARCH, 18, 13, 45);
ZonedDateTime zdt2 = dateTime.atZone(romeZone);//2017-03-18T13:45+01:00[Europe/Rome]
Instant instant = Instant.now();
ZonedDateTime zdt3 = instant.atZone(romeZone);//2023-08-13T16:58:53.994+02:00[Europe/Rome]
9. 时间操作
LocalDate localDate1 = LocalDate.of(2017, 9, 21);
LocalDate localDate2 = localDate1.plusWeeks(1);//一周后 2017-09-28
LocalDate localDate3 = localDate1.minusYears(6);//6年前 2011-09-21
LocalDate localDate4 = localDate1.plus(6, ChronoUnit.MONTHS);//6个月后 2018-03-21
七、函数式接口
1. 定义
接口中只有一个抽象接口的类
2. 如何判断是函数式接口
可以在接口上加注解@FuncitonalInterface来判断是否式函数式接口,如果不报错则是
3. 常见的函数式接口
3.1 Consumer 消费接口
accept(T t)
3.2 Function<T, R> 计算接口
R apply(T t) T是参数,R是返回值
3.3 Predicate<T>判断接口
test(T t ) 返回Boolean
可以使用and/or将两个Predicate连接
3.4 Supplier<T> 生产接口
T get()
4. 方法引用
简化方法引用写法
4.1 使用条件
是函数式接口,方法体里面只有一行
4.2 使用
- 静态方法 String :: valueOf
- 对象实例方法 someObj :: dothing
- 构造方法 StringBuilder :: new
=========================================================================
创作不易,请勿直接盗用,使用请标明转载出处。
喜欢的话,一键三连,您的支持是我一直坚持高质量创作的原动力。