Java8新特性

一、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

=========================================================================创作不易,请勿直接盗用,使用请标明转载出处。

喜欢的话,一键三连,您的支持是我一直坚持高质量创作的原动力。

  • 36
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值