• 2020年学2014年的Java8。QAQ 流下了不学无术的眼泪...

前言

  • 哈希算法:数组-链表/红黑树(链表链接的数据大于8,就会转为红黑树,树上的数据减到6,又会转回链表) 加载因子:0.75
  • 主流Java虚拟机

    • Oracle-SUN HotSpot JVM
    • Oracle JRocket
    • IBM J9 JVM
    • Taobao JVM
    • ...

Lambda表达式

  • 替代匿名内部类
  • 减少冗余代码

优化方式:

  1. 策略模式

    1. 创建一个接口。
    2. 创建实现接口的实体类。
  2. 匿名内部类实现接口
  3. Lambda表达式
  4. Steam API

语法

Lambda表达式的参数列表 -> Lambda表达式需要执行的功能(Lambda体)

左侧:接口中抽象方法的参数列表右侧:需要对抽象方法实现的功能

  1. 无参数,无返回

            Runnable r1 = () -> System.out.print("hello");
            r1.run();
  2. 一个参数,无返回值

            Consumer<String> con = (x) -> System.out.println(x);
            con.accept("hello");
  3. 多个参数,多条语句,有返回值

            Comparator<Integer> com = (x, y) -> {
                System.out.print("hello");
                return Integer.compare(x, y);
            };
    • 如果有一个参数,括号可省略。
    • 如果只有一条语句,return和大括号都可省略
    • 如果写参数类型,需要都写。JVM可以进行“类型推断”

函数式接口

Lambda需要函数式接口的支持。接口中只有一个抽象方法。@FunctionInterface

Example

public class tmp {
    public static void main(String[] args) {
        Integer num = operation(100, x -> x * x);
        System.out.print(num);
    }
    public static Integer operation(Integer num, MyFun mf) {
        return mf.getValue(num);
    }
}

@FunctionalInterface
interface MyFun {
    public Integer getValue(Integer num);
}

四大内置核心函数式接口

  1. Consumer<T>

    • 消费型接口
    • void accept<T t>
  2. Supplier<T>

    • 供给型接口
    • T get()
  3. Function<T, R>

    • 函数型接口
    • R apply(T t)
  4. Rredicate<T>

    • 断言型接口
    • boolean test(T t)
  5. 其他各种子类

方法引用/构造器引用/数组引用

方法引用

Lambda的另一种使用形式(参数和返回值要完全相同)

  • 对象::实例方法名
        Consumer<String> con = System.out::println;
        con.accept("hello");
  • 类::静态方法名
        Comparator<Inteer> com = (x, y) -> Integer.compare(x, y);
        Comparator<Inteer> com1 = Integer::compare;
  • 类::实例方法名
        //第一个参数是实例方法的调用者,第二个参数是实例方法的参数时
        BiPredicate<String, String> bp = (x, y) -> x.equals(y);
        BiPredicate<String, String> dp1 = String::equals;

构造器引用

ClassName::new

        //调用的构造器的参数列表,和函数式接口中抽象类方法的列表保持一致
        Supplier<Employee> sup = () -> new Employee();
        Supplier<Employee> sup2 = Employee::new;

数组引用

        Function<Integer, String[]> fun = x -> new String[x];
        Function<Integer, String[]> fun1 = String[]::new;

Java 8 Stream

有点SQL语句的感觉

  • Stream自己不会存储元素
  • Stream不会改变源对象,会返回经过处理后的新Stream
  • 延迟操作,需要结果的时候才会执行

步骤:

  1. 创建Stream(数据源)
  2. 中间操作(对数据源进行处理)
  3. 终止操作(执行中间操作链,产生结果)

创建Stream

        // 1. 通过collection提供的stream()串行流和parallelStream()并行流
        List<Integer> list = new ArrayList<Integer>();
        Stream<Integer> stream1 = list.stream();
        // 2. 通过Arrlist里的静态方法stream(数组流)
        Employee[] emps = new Employee[10];
        Stream<Employee> stream2 = Array.stream(emps);
        // 3. 通过Stream类的静态方法of()
        Stream<String> stream3 = Stream.of("aa", "bb", "cc");
        // 4. 创建无限流
        // 迭代
        Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
        // 生成
        Stream.generate(()->Math.random())
              .limit(5)
              .forEach(System.out::println);

中间操作

筛选与切片

  1. filter:接收Lambda,从流中排除某系元素。
  2. limit:截断流,使其元素不超过给定数量。
  3. skip(n):跳过元素,返回一个删除前n个元素的流,如果流中元素不足n个,则返回空流。
  4. distinct:筛选,通过流生成的元素hashcode()和equals()去除重复元素。自动构造上述方法。
        // 短路,找到符号条件就结束迭代
        Stream<Integer> s = Stream.iterate(0, x -> x + 2);
        s.limit(10)
         .filter(e -> e > 5)
         .skip(2)   //跳过前两个
         .forEach(System.out::println);

映射

  1. map:接收lambda,将元素转换成其他形式或提取信息。接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  2. flatMap:接收一个函数作为参数,将流中的每一个值都换成另一个流,然后把所有流连接成一个流。
        List<String> list = Arrays.asList("aaa", "bbb", "ccc");
        list.stream()
            .map(str->str.toUpperCase())
            .forEach(System.out::print);
            //flatMap类比集合操作add和addAll

排序

  1. sorted():自然排序(字典顺序)
  2. sorted(Comparator com):定制排序
        List<String> list = Arrays.asList("b", "a", "c", "z", "w");
        list.stream()
            .sorted()
            .forEach(System.out::println);
        List<String> list = Arrays.asList("b", "a", "c", "z", "w");
        list.stream().sorted((e1, e2) -> {
            if (e1.indexOf(0) > e2.indexOf(0))
                return 1;
            else
                return -1;

        }).forEach(System.out::println);

终止操作

查找/匹配

allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素 return Optional<>
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值

归约/收集

reduce——将流中的元素反复结合起来,得到一个值
collect——将流转换成其他形式。接受一个Collector接口的实现,用于给Stream中元素做汇总的方法

        //求1到100奇数的和
        int sum = Stream.iterate(0, x -> x + 1)
                .limit(100)
                .filter(x -> x % 2 == 1)
                .reduce(0, Integer::sum);
        System.out.print(sum);
        List<String> list = employees.stream()
                                     .map(Employee::getName)
                                     .collect(Collectors.toList());

        Double avg = employees.stream()
                              .collect(Collectors.averagingDouble(Employee::getSalary));

Example

 public class TestStreamAPI2 {
      List<Employee> emps = Arrays.asList(
       new Employee(102, "李四", 59, 6666.66, Status.BUSY),
       new Employee(101, "张三", 18, 9999.99, Status.FREE),
       new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
       new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
       new Employee(104, "赵六", 8, 7777.77, Status.FREE),
       new Employee(104, "赵六", 8, 7777.77, Status.FREE),
       new Employee(105, "田七", 38, 5555.55, Status.BUSY)
     );
   
     //3. 终止操作
     /*
      allMatch——检查是否匹配所有元素
      anyMatch——检查是否至少匹配一个元素
      noneMatch——检查是否没有匹配的元素
      findFirst——返回第一个元素
      findAny——返回当前流中的任意元素
      count——返回流中元素的总个数
      max——返回流中最大值
      min——返回流中最小值
      */
     @Test
     public void test1(){
       boolean bl = emps.stream()
        .allMatch((e) -> e.getStatus().equals(Status.BUSY));
     
       System.out.println(bl);
     
       boolean bl1 = emps.stream()
        .anyMatch((e) -> e.getStatus().equals(Status.BUSY));
     
       System.out.println(bl1);
     
       boolean bl2 = emps.stream()
        .noneMatch((e) -> e.getStatus().equals(Status.BUSY));
     
       System.out.println(bl2);
     }  @Test
     public void test2(){
      Optional<Employee> op = emps.stream()
       .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
       .findFirst();
    
      System.out.println(op.get());
    
      System.out.println("--------------------------------");
    
      Optional<Employee> op2 = emps.parallelStream()
       .filter((e) -> e.getStatus().equals(Status.FREE))
       .findAny();
    
      System.out.println(op2.get());
     }  @Test
     public void test3(){
      long count = emps.stream()
           .filter((e) -> e.getStatus().equals(Status.FREE))
           .count();
    
      System.out.println(count);
    
      Optional<Double> op = emps.stream()
       .map(Employee::getSalary)
       .max(Double::compare);
    
      System.out.println(op.get());
    
      Optional<Employee> op2 = emps.stream()
       .min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
    
      System.out.println(op2.get());
     }  // 注意:流进行了终止操作后,不能再次使用
     @Test
     public void test4(){
      Stream<Employee> stream = emps.stream()
       .filter((e) -> e.getStatus().equals(Status.FREE));
    
      long count = stream.count();
    
      stream.map(Employee::getSalary)
       .max(Double::compare);
     }
} 

public class TestStreamAPI3 {
      List<Employee> emps = Arrays.asList(
       new Employee(102, "李四", 79, 6666.66, Status.BUSY),
       new Employee(101, "张三", 18, 9999.99, Status.FREE),
       new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
       new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
       new Employee(104, "赵六", 8, 7777.77, Status.FREE),
       new Employee(104, "赵六", 8, 7777.77, Status.FREE),
       new Employee(105, "田七", 38, 5555.55, Status.BUSY)
     );
   
     //3. 终止操作
     /*
      归约
      reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。
      */
     @Test
     public void test1(){
      List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    
      Integer sum = list.stream()
       .reduce(0, (x, y) -> x + y);
    
      System.out.println(sum);
    
      System.out.println("----------------------------------------");
    
      Optional<Double> op = emps.stream()
       .map(Employee::getSalary)
       .reduce(Double::sum);
    
      System.out.println(op.get());
     }  // 需求:搜索名字中 “六” 出现的次数
     @Test
     public void test2(){
      Optional<Integer> sum = emps.stream()
       .map(Employee::getName)
       .flatMap(TestStreamAPI1::filterCharacter)
       .map((ch) -> {
        if(ch.equals('六'))
         return 1;
        else 
         return 0;
       }).reduce(Integer::sum);
    
      System.out.println(sum.get());
     }  // collect——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
     @Test
     public void test3(){
      List<String> list = emps.stream()
       .map(Employee::getName)
       .collect(Collectors.toList());
    
      list.forEach(System.out::println);
    
      System.out.println("----------------------------------");
    
      Set<String> set = emps.stream()
       .map(Employee::getName)
       .collect(Collectors.toSet());
    
      set.forEach(System.out::println);
  
      System.out.println("----------------------------------");
    
      HashSet<String> hs = emps.stream()
       .map(Employee::getName)
       .collect(Collectors.toCollection(HashSet::new));
    
      hs.forEach(System.out::println);
     }  @Test
     public void test4(){
      Optional<Double> max = emps.stream()
       .map(Employee::getSalary)
       .collect(Collectors.maxBy(Double::compare));
    
      System.out.println(max.get());
    
      Optional<Employee> op = emps.stream()
       .collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
    
      System.out.println(op.get());
    
      Double sum = emps.stream()
       .collect(Collectors.summingDouble(Employee::getSalary));
    
      System.out.println(sum);
    
      Double avg = emps.stream()
       .collect(Collectors.averagingDouble(Employee::getSalary));
    
      System.out.println(avg);
    
      Long count = emps.stream()
       .collect(Collectors.counting());
    
      System.out.println(count);
    
      System.out.println("--------------------------------------------");
    
      DoubleSummaryStatistics dss = emps.stream()
       .collect(Collectors.summarizingDouble(Employee::getSalary));
    
      System.out.println(dss.getMax());
     }  // 分组
     @Test
     public void test5(){
      Map<Status, List<Employee>> map = emps.stream()
       .collect(Collectors.groupingBy(Employee::getStatus));
    
      System.out.println(map);
     }  // 多级分组
     @Test
     public void test6(){
      Map<Status, Map<String, List<Employee>>> map = emps.stream()
       .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
        if(e.getAge() >= 60)
         return "老年";
        else if(e.getAge() >= 35)
         return "中年";
        else
         return "成年";
       })));
    
      System.out.println(map);
     }  // 分区
     @Test
     public void test7(){
      Map<Boolean, List<Employee>> map = emps.stream()
       .collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
    
      System.out.println(map);
     }  //
     @Test
     public void test8(){
      String str = emps.stream()
       .map(Employee::getName)
       .collect(Collectors.joining("," , "----", "----"));
    
      System.out.println(str);
     }  @Test
     public void test9(){
      Optional<Double> sum = emps.stream()
       .map(Employee::getSalary)
       .collect(Collectors.reducing(Double::sum));
    
      System.out.println(sum.get());
     }
}

并行流

  • Fork/Join 框架

Fork/Join 框架是 Java7 提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。

任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务(工作窃取算法)。

传统线程存在的问题:没任务的线程空闲

此处省略Java7中的实现

Stream API可以通过 parallel()sequential()在并行流与顺序流之间切换

        Instant start = Instant.now();
        LongStream.rangeClosed(0, 100000000000L)
                  .parallel()
                  .reduce(0, Long::sum);
        Instant end = Instant.now();
        System.out.println(Duration.between(start, end));
        //20.276S

Optional类

Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常。

常用方法:
Optional.of(T t):创建一个Optional实例
Optional.empty():创建一个空的Optional实例
Optional.ofNullable(T t):若t不为null,创建Optional实例,否则创建空的实例
isPresent():判断是否包含值
orElse(T t):如果Optional实例包含值,返回该值,否则返回t
orElseGet(Supplier s):如果Optional实例包含值,返回该值,否则返回s获取的值
map(Function f):如果有值对其处理,并返回成立后的Optional,否则返回Optional.empty()
flatMap(Function mapper):与map类似,要求返回值必须是Optional

public class OptionalAPI {
    @Test
    public void test01(){
        //Optional.of(T t):创建一个Optional实例
        Optional<Employee> optional = Optional.of(new Employee());
        Employee employee = optional.get();
        System.out.println(employee);
    }

    @Test
    public void test02(){
        //Optional.ofNullable(T t):若t不为null,创建Optional实例,否则创建空的实例
        Optional<Object> optional = Optional.ofNullable(null);
        //isPresent():判断是否包含值
        if (optional.isPresent()){
            System.out.println("值是:"+optional.get());
        }
        //orElse(T t):如果调用对象包含值,返回该值,否则返回t
        System.out.println(optional.orElse(new Employee("张三",22,9999.99)));
        //orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回s获取的值
        Employee o = (Employee) optional.orElseGet(Employee::new);
        System.out.println(o);
    }

    @Test
    public void test03(){
        //map(Function f):如果有值对其处理,并返回成立后的Optional,否则返回Optional.empty()
        Optional<Employee> optional = Optional.of(new Employee("张三", 22, 9999.99));
        Optional<String> optionalS = optional.map(Employee::getName);
        System.out.println(optionalS.get());
        //flatMap(Function mapper):与map类似,要求返回值必须是Optional
        Optional<String> stringOptional = optional.flatMap((e) -> Optional.of(e.getName()));
        System.out.println(stringOptional.get());
    }
}

接口中的默认方法和静态方法

interface MyInterface {
    // 默认方法
    default String getName() {
        return "hello";
    }
    // 静态方法
    public static void show() {
        System.out.print("hello");
    }
}

新的时间日期API

java.time.*
解决线程安全问题

本地时间/时间戳

public class TestLocalDateTime {
    /*
     * 1.LocalDate 本地日期
     * 2.LocalTime 本地时间
     * 3.LocalDateTime 本地日期与时间
     *
     * 本测试是使用LocalDateTime为例,另外两个和这个一样
     */
    @Test
    public void test01(){
        //获取当前时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);
        //指定本地时间
        LocalDateTime time = LocalDateTime.of(2020, 2, 1, 13, 22, 33);
        System.out.println(time);
        //日期运算
        //在当前的日期上加8年
        LocalDateTime nowPlus = now.plusYears(8);
        System.out.println(nowPlus);
        //在当前的日期上减8天
        LocalDateTime nowMinus = now.minusDays(8);
        System.out.println(nowMinus);
        //获取时间
        //获取年份
        System.out.println(now.getYear());
        /*
         *获取月份,注意得到月份有两个方法,一个是getMonth(),得到Month这个
         *枚举类,可以用这个类的getValue()方法得到月份,而getMonthValue()
         *可以直接得到月份
         */
        System.out.println(now.getMonthValue());
        System.out.println(now.getMonth().getValue());
        //得到天数
        System.out.println(now.getDayOfMonth());
        //得到小时
        System.out.println(now.getHour());
        //得到分钟
        System.out.println(now.getMinute());
        //得到秒
        System.out.println(now.getSecond());
    }

    /*
     * Instant:时间戳(以Unix元年:1970年1月1日00:00:00到某个时间之间的毫秒值)
     * 计算机读到的时间
     */
    @Test
    public void test02(){
        //以UTC时间
        Instant now = Instant.now();
        System.out.println(now);

        //做偏移量运算
        OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8));
        System.out.println(offsetDateTime);

        //转为毫秒值
        System.out.println(now.toEpochMilli());

        //结果1970-01-01T00:01:00Z
        Instant instant = Instant.ofEpochSecond(60);
        System.out.println(instant);
    }

    /*
     * Duration:计算两个“时间”之间的间隔
     * period:计算两个“日期”之间的间隔
     */
    @Test
    public void test03(){
        Instant instant1 = Instant.now();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Instant instant2 = Instant.now();
        Duration between = Duration.between(instant1, instant2);
        //得到两个时间之间相差多少秒
        System.out.println(between.getSeconds());
    }

    /*
     * period:计算两个“日期”之间的间隔
     */
    @Test
    public void test04(){
        LocalDate localDate1 = LocalDate.of(2020,1,1);
        LocalDate localDate2 = LocalDate.now();
        Period between = Period.between(localDate1, localDate2);
        System.out.println(between.getMonths());
    }
}

时间校正器

public class TestTemporalAdjuster {
    /*
     * TemporalAdjuster:时间校正器。有时我们可以需要获取例如:将时间调整到“下个周末”等操作。
     * TemporalAdjusters:该类通过静态方法提供了大量的常用TemporalAdjuster的实现。
     */
    @Test
    public void test05() {
        LocalDateTime ldt = LocalDateTime.ldt();
        System.out.println(ldt);
        // 把当前时间的天数改为8,如2020-02-01T16:36:46.533改为2020-02-08T16:36:46.533
        LocalDateTime localDateTime = ldt.withDayOfMonth(8);
        System.out.println(localDateTime);
        // 把当前时间调整为下周六
        LocalDateTime ldt2 = ldt.with(TemporalAdjusters.next(DayOfWeek.SATURDAY));
        System.out.println(ldt2);

        // 自定义:下一个结婚纪念日,今天当作纪念日,再过12*30天就是下一个结婚纪念日
        LocalDateTime time = ldt.with((t) -> {
            LocalDateTime marryTime = (LocalDateTime) t;
            LocalDateTime nextMarryTime = marryTime.plusDays(12 * 30);
            return nextMarryTime;
        });
        System.out.println(time);
    }
}

时间格式化和日期处理

public class TestDateTimeFormatter {
    /*
     * DateTimeFormatter:格式化时间/日期
     */
    @Test
    public void test01() {
        // 使用默认的格式化
        DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now.format(formatter));
        // 使用自定义的
        DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        System.out.println(now.format(formatter1));
        LocalDateTime parse = LocalDateTime.parse(now.format(formatter1), formatter1);
        System.out.println(parse);
    }

    /*
     * java8中加入了对时区的支持,带时区的时间分别为: ZonedDate、ZonedTime、ZonedDateTime
     * 其中每个时区都对应着ID,地区ID都为“{区域}/{城市}”的格式 例如:Asia/Shanghai等 ZoneId:该类中包含了所有的时区信息
     * getAvailableZonelds():可以获取所有时区信息 of(id):用指定的时区信息获取ZoneId对象
     */
    @Test
    public void test02() {
        // 有多少可以支持的时区
        Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        availableZoneIds.forEach(System.out::println);
    }

    @Test
    public void test03() {
        // 指定一个时区构建时间
        LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Rangoon"));
        System.out.println(now);
        // 构建带时区的时间
        ZonedDateTime zonedDateTime = now.atZone(ZoneId.of("Asia/Rangoon"));
        System.out.println(zonedDateTime);
    }
}

重复注解/类型注解

public class TestAnnotation {
    @MyAnnotation("Hello")
    @MyAnnotation("World")
    public void show() {

    }

    @Test
    public void test01() throws NoSuchMethodException {
        Class<TestAnnotation> testAnnotationClass = TestAnnotation.class;
        Method show = testAnnotationClass.getMethod("show");
        MyAnnotation[] annotationsByType = show.getAnnotationsByType(MyAnnotation.class);
        Arrays.stream(annotationsByType).map(MyAnnotation::value).forEach(System.out::println);
    }
}

// 指定容器类
@Repeatable(MyAnnotations.class)
@Target({ TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE })
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
    String value() default "annotation";
}

@Target({ TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE })
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotations {
    MyAnnotation[] value();
}

参考链接:




扫一扫在手机打开当前页