一、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
=========================================================================创作不易,请勿直接盗用,使用请标明转载出处。
喜欢的话,一键三连,您的支持是我一直坚持高质量创作的原动力。