JDK1.8新特性

主要内容

新特性简介

  • 速度更快
  • 代码更少(lambda表达式)
  • 强大的Stream API
  • 便于并行
  • 最大化减少空指针异常 Optional

1. lambda表达式

为什么使用lambda表达式

  • Lambda是一个匿名函数,我们可以把lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递),写出更简洁,更灵活的代码。作为一种更紧凑的代码风格,使java的语言表达能力得到了提升。

  • 基本语法


/**
 *  java8 引入了一个新的操作符"->" ,该操作符称为箭头操作符或lambda操作符
 *              箭头操作符将Lambda表达式拆分成两部分:
 *
 *        左侧: lambda的参数列表
 *        右侧: lambda表达式需要执行的功能,即lambda体
 *
 *      语法格式一: 接口无参数无返回值 () -> System.out.println("lambda");
 *      语法格式二: 有一个参数,并且无返回值
 *      语法格式三: 只有一个参数,小括号可以省略不写
 *      语法格式四: 有两个以上的参数,并且lambda中有多条语句
 *      语法格式五: 若lambda体中只有一条语句,return 和大括号可以省略不写
 *      语法格式六: lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即"类型推断"
 *
 *  顺口溜: 左右遇一括号省,左侧推断类型省
 *
 *  二, lambda 表达式需要"函数式接口的支持"
 *      函数式接口: 若接口中只有一个抽象方法时,称为函数式接口, 可以使用注解@FunctionalInterface修饰: 检查一个接口是不是函数式接口
 *
 */

public class TestLambda { 

    /**
     *  语法格式一: 接口无参数无返回值 () -> System.out.println("lambda");
     */
    @Test
    public void test(){
        //jdk1.8 以前 匿名内部类在应用局部变量时必须是final
        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("hello,world");
            }
        };
        runnable1.run();
        System.out.println("------------------lambda------------------");
        Runnable runnable = () -> System.out.println("hello, world!");
        runnable.run();
    }


    /**
     * 语法格式二: 有一个参数,并且无返回值
     */
    @Test
    public void test01(){
        Consumer<String> consumer = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        consumer.accept("hello");

        System.out.println("------------------lambda------------------");

        Consumer<String> consumer01 = (s) -> System.out.println(s);
        consumer01.accept("hello");
    }

    /**
     * 语法格式三: 只有一个参数,小括号可以省略不写
     */
    @Test
    public void test02(){
        System.out.println("------------------lambda------------------");

        Consumer<String> consumer01 = s -> System.out.println(s);
        consumer01.accept("hello");
    }

    /**
     * 语法格式四: 有两个以上的参数,并且lambda中有多条语句
     */
    @Test
    public void test03(){
        Comparator<Integer> comparator01 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                System.out.println("o1 ->" + o1 );
                System.out.println("o2 ->" + o2 );
                return o1 > o2 ? 1 : 0;
            }
        };
        comparator01.compare(10, 12);
        System.out.println("------------------lambda------------------");

        Comparator<Integer> com = (o1,o2) -> {
            System.out.println("o1 ->" + o1 );
            System.out.println("o2 ->" + o2 );
            return o1 > o2 ? 1 : 0;
        };
        com.compare(12, 10);
    }

    /**
     * 语法格式五: 若lambda体中只有一条语句,return 和大括号可以省略不写
     */
    @Test
    public void test04(){
        System.out.println("------------------lambda------------------");

        Comparator<Integer> com = (o1,o2) -> Integer.compare(o1,o2);
        com.compare(12, 10);
    }

    /**
     * 语法格式六: lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即"类型推断"
     */
    @Test
    public void test05(){
        System.out.println("------------------lambda------------------");
        Comparator<Integer> com = (o1,o2) -> Integer.compare(o1,o2);
        com.compare(12, 10);
    }
}

2.四大内置核心函数式接口


/**
 * java8 内置四大核心函数式接口
 * <p>
 * <p>
 * Consumer<T> : 消费性接口
 * void accept(T t);
 * Supplier<T>
 * <p>
 * Function(T, R) : 函数性接口
 * R apply(T t);
 * <p>
 * Predicate<T> :断言型接口
 * boolean test(T t);
 */
public class TestLambda01 {

    //Consumer<T> 消费性接口
    @Test
    public void test01() {
        happy(100000, (m) -> {
            System.out.println("消费了100000元");
        });
    }

    public void happy(double money, Consumer<Double> n) {
        n.accept(money);
    }

    //Supplier<T> 供给型接口
    @Test
    public void test02() {
        List<Integer> numList = getNumList(10, () -> (int) Math.random() * 100);
    }

    //需求: 产生指定个数的整数,并放入集合中
    public List<Integer> getNumList(int num, Supplier<Integer> supplier) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            list.add(supplier.get());
        }
        return list;
    }

    //Function(T, R) : 函数性接口
    @Test
    public void test03() {
        String hello = strHandler("hello     ", (s) -> s.trim());
        System.out.println(hello);
    }
    
    public String strHandler(String str, Function<String, String> f) {
        return f.apply(str);
    }

    //Predicate<T> :断言型接口
    //需求 : 将满足条件的字符串,放入集合中
    public List<String> filterStr(List<String> list, Predicate<String> predicate) {
        List<String> strs = new ArrayList<>();
        for (String str : list) {
            if (predicate.test(str)) {
                strs.add(str);
            }
        }
        return strs;
    }

    //将不是222的字符串放入list
    @Test
    public void test04() {
        filterStr(Arrays.asList("111","222","333","aaa","bbb"), (s) -> !s.equals("222"));
    }
}

在这里插入图片描述

3.方法引用与构造器引用

/**
 * 方法引用: 若lambda体中的内容有方法已经实现了,我们可以使用方法引用
 * (可以理解为方法引用是lambda表达式的另外一种表现形式)
 *
 * 主要有三种语法格式
 *
 * 对象::实例方法名
 *
 * 类::静态方法名
 *
 * 类::实例方法名
 *
 * 构造器引用
 *
 * 数组引用
 *
 */
public class TestLambda02 {

    //对象::实例方法名
    @Test
    public void test() {
        PrintStream ps = System.out;

        Consumer<String> con = (s) -> ps.println(s);
        System.out.println("----------实例方法-------------");
        //注意事项: 参数列表和返回值要和接口方法一致
        Consumer<String> c = ps::println;
        Consumer<String> c1 = System.out::println;
    }

    //类::静态方法名
    public void test1() {
        Comparator<Integer> com = (x, y) -> Integer.compare(x, y);

        Comparator<Integer> c = Integer::compare;
    }

    //类::实例方法名
    //当第一个参数是对象,第二个参数是参数传入如(x, y) -> x.equals(y);
    //这时就可以使用类::实例方法名
    public void test2() {
        BiPredicate<String, String> bp = (x, y) -> x.equals(y);

        BiPredicate<String, String> bp2 = String::equals;
    }

    //构造器引用
    public void test3(){
        Supplier<Employee> supplier = () -> new Employee();
        Employee employee = supplier.get();


        //构造器调用方式 函数时接口方法的参数列表匹配构造器参数列表
        Supplier<Employee> supplier1 = Employee::new;
        supplier1.get();
    }

    //数组引用
    public void test4(){
        Function<Integer,String[]> fun = String[]::new;
        String[] apply = fun.apply(10);
        System.out.println(apply.length);
    }
}

4.强大的Stream API

什么是Stream

流到底是什么

​ 是数据渠道,用于操作数据源(集合,数组等)所生成的元素序列

集合讲的是数据,流讲的是计算

注意

  • Stream 自己不会存储元素
  • Stream 不会改变元对象,相反,它们会返回一个新的Stream
  • Stream 操作时延迟执行的,这意味着他们会等到需要结果时才执行(单例模式:懒汉)

如何使用

  • 创建stream (从一个集合,数组中获取一个Stream)
  • 中间操作(对数据元中的数据进行操作处理)
  • 终止操作 (执行中间操作链,返回结果)
    在这里插入图片描述

创建流

/**
 * 一,Stream的三个操作步骤
 *
 * 1. 创建Stream
 *
 * 2. 中间操作
 *
 * 3. 终止操作
 */
public class StreamAPI01 {

    //创建Stream
    public void test1(){
        //1. 可以通过Collection系列集合提供的Stream() 串行流或ParallelStream()获取流 并行流
        List<String> list = new ArrayList<>();
        //获取串行流
        Stream<String> stream = list.stream();

        //2. 通过Arrays中的静态方法获取流
        int[] emps = {1,2,3,4,5};
        IntStream stream1 = Arrays.stream(emps);

        //3. 通过Stream的静态方法of()
        Stream<String> aa = Stream.of("aa", "bb", "bb");

        //4. 创建无限流
        //迭代
        Stream.iterate(0, (x) -> x + 2).forEach(System.out::println);
        //生成
        Stream<Integer> generate = Stream.generate(() -> (int) Math.random());
    }
}

筛选与切片

/**
 * 筛选与切片
 *
 * filter --- 接收lambda.从流中排除一些元素
 * limit --- 截断流,使其元素不超过给定数量
 * skip(n) --- 跳过元素,返回一个扔掉了前n个元素的流,若流中元素b不足n个,返回一个空流
 * distinct --- 筛选,通过流所生成的hashcode()和equals()方法去除重复元素
 */
public class StreamAPI02 {

    List<Employee> employees = Arrays.asList(
            new Employee("张三", 20, 35000.0),
            new Employee("李四", 39, 35000.0),
            new Employee("李四", 39, 35000.0),
            new Employee("李四", 39, 35000.0),
            new Employee("王五", 38, 35000.0),
            new Employee("张麻子", 38, 35000.0),
            new Employee("张麻子", 38, 35000.0),
            new Employee("张麻子", 38, 35000.0),
            new Employee("张麻子", 38, 35000.0),
            new Employee("张麻子", 38, 35000.0),
            new Employee("雪乃", 20, 35000.0)
    );

    /**
     * 从流中筛选年龄大于30的元素
     */
    @Test
    public void test(){
        Stream<Employee> employeeStream = employees.stream().filter((e) -> e.getAge() > 30);
        employeeStream.forEach(System.out::println);
    }
    /**
     * 从流中获取2个元素
     */
    @Test
    public void test1(){
        Stream<Employee> employeeStream = employees.stream().limit(2);
        employeeStream.forEach(System.out::println);
    }

    /**
     * 从流中扔掉n个元素
     */
    @Test
    public void test2(){

        Stream<Employee> employeeStream = employees.stream().skip(5);
        employeeStream.forEach(System.out::println);
    }

    /**
     * 去重 ,泛型对象必须重写hashcode和equals方法
     */
    @Test
    public void test3(){

        Stream<Employee> employeeStream = employees.stream().distinct();
        employeeStream.forEach(System.out::println);
    }
}

映射


public class StreamAPI03 {

    /**
     * 映射:
     *  map ---接收lambda ,将元素转换成其他形式或提取信息,接收一个函数作为参数,该函数会被应用到每个元素上将其映射为一个新的元素
     *  flatmap --- 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有的流连接成一个流
     */

    List<Employee> employees = Arrays.asList(
            new Employee("张三", 20, 35000.0),
            new Employee("李四", 39, 35000.0),
            new Employee("李四", 39, 35000.0),
            new Employee("李四", 39, 35000.0),
            new Employee("王五", 38, 35000.0),
            new Employee("张麻子", 38, 35000.0),
            new Employee("张麻子", 38, 35000.0),
            new Employee("张麻子", 38, 35000.0),
            new Employee("张麻子", 38, 35000.0),
            new Employee("张麻子", 38, 35000.0),
            new Employee("雪乃", 20, 35000.0)
    );
    /**
     * 将所有的元素转换成大写
     */
    @Test
    public void test(){
        List<String> strings = Arrays.asList("aaa","bbb","ccc","ddd");
        strings.stream().map((str) -> str.toUpperCase()).forEach(System.out::println);
    }

    /**
     * 获取所有员工的姓名
     * 并去重
     */
    @Test
    public void test1(){
        employees.stream().map((e) -> e.getName()).forEach(System.out::println);
    }

    public static Stream<Character> filterCharacter(String str){
        List<Character> list = new ArrayList<>();
        for (Character character : str.toCharArray()) {
            list.add(character);
        }
        return list.stream();
    }

    /**
     * flatmap --- 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有的流连接成一个流
     */
    @Test
    public void test2(){
        List<String> strings = Arrays.asList("aaa","bbb","ccc","ddd");
        System.out.println(strings);
        Stream<Stream<Character>> streamStream = strings.stream().map(StreamAPI03::filterCharacter);

        streamStream.forEach((stream) -> stream.forEach(System.out::println));
        System.out.println("--------------------------flatmap------------------------");
        strings.stream().flatMap(StreamAPI03::filterCharacter).forEach(System.out::println);
    }
}

排序

public class StreamAPI04 {

    /**
     * 排序
     * sorted() --- 自然排序(comparable)
     * sorted(comparable com) --- 定制排序
     */

    List<Employee> employees = Arrays.asList(
            new Employee("张三", 20, 35000.0),
            new Employee("李四", 39, 35000.0),
            new Employee("李四", 39, 35000.0),
            new Employee("李四", 39, 35000.0),
            new Employee("王五", 38, 35000.0),
            new Employee("张麻子", 38, 35000.0),
            new Employee("张麻子", 38, 35000.0),
            new Employee("张麻子", 38, 35000.0),
            new Employee("张麻子", 38, 35000.0),
            new Employee("张麻子", 38, 35000.0),
            new Employee("雪乃", 20, 35000.0)
    );

    /**
     * 自然排序(comparable)
     */
    @Test
    public void test() {
        List<String> strings = Arrays.asList("bbb", "ddd", "aaa", "ccc");
        strings.stream().sorted().forEach(System.out::println);

    }

    /**
     * 定制排序
     */
    @Test
    public void test1() {
        employees.stream().sorted((e1, e2) -> {
            if (e1.getAge() == e2.getAge()) {
                //如果年龄相等比较姓名
                if (e1.getName().equals(e2.getName())) {
                //如果年龄和姓名相等比较工资
                    return Double.compare(e1.getSalary(),e2.getSalary());
                }
                return e1.getName().compareTo(e2.getName());
            }
            return Integer.compare(e1.getAge(), e2.getAge());
        }).forEach(System.out::println);
    }
}

查找与匹配

/**
 * 终止操作
 */
public class StreamAPI05 {


    List<Employee> employees = Arrays.asList(
            new Employee("张三", 20, 35000.0, Employee.Status.BUSY),
            new Employee("李四", 39, 444444.0, Employee.Status.FREE),
            new Employee("王五", 38, 22222.0,Employee.Status.BUSY),
            new Employee("张麻子", 38, 3000.0,Employee.Status.VOCATION),
            new Employee("雪乃", 20, 500000.0,Employee.Status.FREE)
    );
    /**
     * 查找与匹配
     * allMatch --- 检查是否匹配所有元素
     * anyMatch --- 检查是否至少匹配一个元素
     * noneMatch --- 检查是否没有匹配的所有元素
     * findFirst --- 返回第一个元素
     * findAny --- 返回流中的任意元素
     * count --- 返回流中的总个数
     * max --- 返回流中最大值
     * min --- 返回流中最小值
     */
    @Test
    public void test(){
        //所有员工的状态是否都在忙碌?
        boolean b = employees.stream().allMatch(employee -> employee.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b);
        //有员工处于忙碌?
        b = employees.stream().anyMatch(employee -> employee.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b);
        //是否没有员工在休息
        b = employees.stream().noneMatch(employee -> employee.getStatus().equals(Employee.Status.VOCATION));
        System.out.println(b);
        //获取第一个员工
        //Optional避免空指针
        Optional<Employee> first = employees.stream().findFirst();
        System.out.println(first.get());
        //随机获取一个元素 串行口中找到第一个就返回
        Optional<Employee> any = employees.stream().findAny();
        System.out.println(any.get());
        //并行的流中,findAny返回的是最快处理完的那个线程的数据
        Optional<Employee> random = employees.parallelStream().findAny();
        System.out.println(random.get());
        //返回工资最高的员工
        Optional<Employee> max = employees.stream().max(Comparator.comparingDouble(Employee::getSalary));
        System.out.println("员工工资最高的是 ===>" + max.get());
        //返回工资最低的员工
        Optional<Employee> min = employees.stream().min(Comparator.comparingDouble(Employee::getSalary));
        System.out.println("员工工资最低的是 ===>" + min.get());
    }
}

规约和收集

/**
 * 终止操作
 */
public class StreamAPI06 {


    List<Employee> employees = Arrays.asList(
            new Employee("张三", 20, 35000.0, Employee.Status.BUSY),
            new Employee("李四", 39, 444444.0, Employee.Status.FREE),
            new Employee("王五", 38, 22222.0, Employee.Status.BUSY),
            new Employee("张麻子", 38, 3000.0, Employee.Status.VOCATION),
            new Employee("雪乃", 20, 500000.0, Employee.Status.FREE)
    );

    /**
     * 规约 reduce(T identity, BinaryOperator) / reduce (BinaryOperator)
     * --- 可以将流中元素反复结合起来,得到一个值 
     */
    @Test
    public void test() {
        //将所有元素累加返回一个结果
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        Integer reduce = list.stream().reduce(0, (x, y) -> x + y);
        System.out.println(reduce);

        //每月需支付的工资
        Optional<Double> reduce1 = employees.stream().map(Employee::getSalary).reduce(Double::sum);
        System.out.println("共需支付的工资为 ===>" + reduce1.get());
    }

    /**
     * 收集
     * collect --- 将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
     */
    public void test1() {
        //把所有员工的名字收集起来放到一个集合中
        List<String> collect = employees.stream().map(Employee::getName).collect(Collectors.toList());
        collect.forEach(System.out::println);
        //把所有员工的名字收集起来放到一个linkedList中
        LinkedList<String> collect1 = employees.stream().map(Employee::getName).collect(Collectors.toCollection(LinkedList::new));
        collect1.forEach(System.out::println);

        //获取平均工资
        Double collect2 = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(collect2);
        //获取工资总和
        Double collect3 = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(collect3);
        //获取工资最高的员工信息
        Optional<Employee> collect4 = employees.stream().collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
        System.out.println(collect4.get());
    }

    /**
     * 分组
     */
    @Test
    public void test3() {
        //根据员工状态将员工分组
        Map<Employee.Status, List<Employee>> collect = employees.stream().collect(Collectors.groupingBy(Employee::getStatus));
        System.out.println(collect);
    }

    /**
     * 多级分组
     */

    @Test
    public void test4() {
        //先按状态分组
        //状态之下按年龄分组
        Map<Employee.Status, Map<String, List<Employee>>> collect = employees.stream().collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy(employee -> {
            if (employee.getAge() < 25) {
                return "青年";
            } else if (employee.getAge() < 35) {
                return "中年";
            }
            return "老年";
        })));
        System.out.println(collect);
    }

    //分片
    @Test
    public void test5(){
        //满足工资大于10000的一个区,不满足的一个区
        Map<Boolean, List<Employee>> collect = employees.stream().collect(Collectors.partitioningBy(employee -> employee.getSalary() > 10000));
        System.out.println(collect);
    }

    //获取对象的一个属性的总结
    @Test
    public void test6(){
        DoubleSummaryStatistics collect = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(collect.getMax());
        System.out.println(collect.getMin());
        System.out.println(collect.getCount());
        System.out.println(collect.getSum());
    }


    //将流对象的一个属性拼接成一个字符串返回

    /***
     * join() 无拼接
     *
     * join(",") 在每个属性的间隔添加,
     *
     * join("连接符","前缀","后缀")
     */
    @Test
    public void test7(){
        String collect = employees.stream().map(Employee::getName).collect(Collectors.joining(",","",""));
        System.out.println(collect);
    }

}

StreamAPI 练习题

/**
 * 终止操作 练习题
 */
public class StreamAPI07 {

    /**
     * 1, 给定一个数字列表,如何返回一个有每个数字的平方构成的列表
     * [1,2,3,4,5] 返回[1,4,9,16,25]
     */
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
    @Test
    public void test() {
        List<Integer> collect = list.stream().map((e) -> e * e).collect(Collectors.toList());
        System.out.println(collect);
    }
    /**
     * 2. 怎样用map和reduce方法数一数流中有多少个Employee
     */
    List<Employee> employees = Arrays.asList(
            new Employee("张三", 20, 35000.0, Employee.Status.BUSY),
            new Employee("李四", 39, 444444.0, Employee.Status.FREE),
            new Employee("王五", 38, 22222.0, Employee.Status.BUSY),
            new Employee("张麻子", 38, 3000.0, Employee.Status.VOCATION),
            new Employee("雪乃", 20, 500000.0, Employee.Status.FREE)
    );
    @Test
    public void test01() {
        Optional<Integer> reduce = employees.stream().map((emp) -> 1).reduce(Integer::sum);
        System.out.println(reduce.get());
    }


    List<Transaction> transactions = null;
    @Before
    public void before() {
        Trader raoul = new Trader("Raoul", "上海");
        Trader mario = new Trader("Mario", "北京");
        Trader alan = new Trader("Alan", "上海");
        Trader brian = new Trader("Brain", "上海");
        transactions = Arrays.asList(
                new Transaction(brian, 2011, 300),
                new Transaction(raoul, 2012, 1000),
                new Transaction(raoul, 2011, 400),
                new Transaction(mario, 2012, 710),
                new Transaction(mario, 2012, 700),
                new Transaction(alan, 2012, 950)
        );
    }
    //1. 找出2011年发生的所有交易,并按照交易额排序,(从高到底)
    @Test
    public void test02() {
        transactions.stream()
                //过滤出2011年的交易
                .filter(e -> (e.getYear() == 2011))
                //逆序按照交易额排序
                .sorted((t1, t2) -> -Integer.compare(t1.getValue(), t2.getValue()))
                .forEach(System.out::println);
    }
    //2. 交易员都在哪些不同的城市工作过
    @Test
    public void test03(){
        transactions.stream()
                //获取交易员
                .map(Transaction::getTrader)
                //获取城市
                .map(Trader::getCity)
                //去重
                .distinct()
                //打印
                .forEach(System.out::println);
    }
    //3. 查找所有来自上海的交易员,并按姓名排序
    @Test
    public void test04(){
        transactions.stream()
                //获取交易员
                .map(Transaction::getTrader)
                //过滤来自上海的
                .filter((trader) -> trader.getCity().equals("上海"))
                //按照姓名排序
                .sorted(Comparator.comparing(Trader::getName))
                //去重
                .distinct()
                .forEach(System.out::println);
    }
    //4. 返回所有交易员的姓名字符串,按照字母顺序排序
    @Test
    public void test05(){
        transactions.stream()
                //获取交易员
                .map(Transaction::getTrader)
                //去重
                .distinct()
                //获取名字
                .map(Trader::getName)
                //将Stream<String> -> Stream<char []>
                .map(str -> str.toCharArray())
                //Stream<char []> -> Stream<Character>
                .flatMap(StreamAPI07::filterCharacter)
                //排序
                .sorted()
                .forEach(System.out::print);

    }
    public static Stream<Character> filterCharacter(char[] str){
        List<Character> list = new ArrayList<>();
        for (Character character : str) {
            list.add(character);
        }
        return list.stream();
    }
    //5. 有没有交易员在北京工作的
    @Test
    public void test06(){
        boolean hasOne = transactions.stream()
                //获取交易员
                .map(Transaction::getTrader)
                //去重
                .distinct()
               //匹配一个
                .anyMatch((t) -> t.getCity().equals("北京"));
        System.out.println(hasOne);
    }
    //6. 打印生活在上海的交易员的所有交易额
    @Test
    public void test07(){
        transactions.stream()
                //过滤上海交易
                .filter(transaction ->
            transaction.getTrader().getCity().equals("上海"))
                //获取交易额
                .map(Transaction::getValue)
                //打印
                .forEach(System.out::println);
    }
    //7. 所有交易中,最高的交易额是多少
    @Test
    public void test08(){
        Optional<Transaction> max = transactions.stream().max(Comparator.comparingInt(Transaction::getValue));
        System.out.println(max.get().getValue());
    }
    //8. 找到交易额最小的交易
    @Test
    public void test09(){
        Optional<Transaction> max = transactions.stream().min(Comparator.comparingInt(Transaction::getValue));
        System.out.println(max.get().getValue());
    }
}

并行流与顺序流

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流
Java8 中将并行化进行了优化,我们可以很容易的对数据进行操作。StreamAPI 可以声明性地通过Parallel()和sequential()在并行流和顺序流中切换

了解Fork/Join框架

在这里插入图片描述

Fork/Join框架: 就是在必要地情况下,将一个大任务,进行fork拆分成若干个小任务,在将一个个小任务运算地结果进行join汇总

  //通过Parallel()和sequential()在并行流和顺序流中切换
@Test
    public void test10(){
        Instant instant = Instant.now();
        //顺序流
        LongStream.range(0, 100000000000L)
                .reduce(0,Long::sum);
        Instant end = Instant.now();
        System.out.println("串行消耗时间为" + Duration.between(instant, end).toMillis());  //串行消耗时间为56913 依据个人电脑而言

        Instant start = Instant.now();
        //并行流
        LongStream.range(0, 100000000000L)
                .parallel()
                .reduce(0,Long::sum);
        Instant end1 = Instant.now();
        System.out.println("并行消耗时间为" + Duration.between(start, end1).toMillis()); //并行消耗时间为35849 依据个人电脑而言
    }

Optional类

optional类是一个容器类,代表一个值存在或者不存在,原来用nullb表示一个值不存在,现在用optional可以更好地表达这个概念,并且可以避免空指针异常

常用方法

optional.of(T t) : 创建一个Optional实例

optional.empty() : 创建一个空的Optional实例

optional.ofNullable(T t );若t不为空,创建Optional实例,否则创建一个空的Optional

isPresent() ; 判断是否为空 : 为空false,不为空true

orElse(T t) : 如果获取Optional中地对象为空返回此方法传入地对象

orElseGet(Supplier s ): 如果调用对象包含该值,返回值,否则返回s获取地值

map(Function f) : 如果有值,对其处理,并返回处理后地Optional,否则返回Optional.empty()

flatMap(Funation mapper): 与map类似,要求返回值必须是Optional

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

在这里插入图片描述

新时间API

时间格式参照表

转自https://blog.csdn.net/wulex/article/details/84062816

元素示例描述
d8不带前导零的日期
dd08日期
M3不带前导零的月份
MM03月份
MMM3 月月份短名称
MMMM三月月份长名称
y6不带前导零的简短年份
yy06简短年份
yyyy2006完整年份
H8(如果时间为 08:15)24小时时间格式中的小时数,不带前导零
HH21(如果时间为 21:30)24小时时间格式中的小时数
h9(如果时间为 21:30)AM/PM时间格式中的小时数,不带前导零
hh09(如果时间为 21:30)AM/PM时间格式中的小时数
m9不带前导零的分钟数
mm9分钟数
s6不带前导零的秒数
ss06秒数
fhh:mm.f 显示 01:25.6十分之一秒
ffhh:mm.ff 显示 01:25.63百分之一秒
fffhh:mm.fff 显示 01:25.634千分之一秒
Z(GMT+01:00)简短时区
ZZ(GMT+01:00) GMT 夏令时完整时区
zHH:mm z 显示 13:25 +1不带前导零的时区时差
zzHH:mm zz 显示 13:25 +01时区时差
zzzHH:mm zz 显示 13:25 +01:00带分钟的时区时差
thh:mm t 显示 01:25 PAM 和 PM 的短格式
tthh:mm tt 显示 01:25 PMAM和PM的长格式
" "“Approximately” hh:mm tt 显示“大约 01:25 PM”用双引号括起文字

API讲解

原文地址https://my.oschina.net/u/4347889/blog/3346877

新时间日期API常用、重要对象介绍
  • ZoneId: 时区ID,用来确定Instant和LocalDateTime互相转换的规则
  • Instant: 用来表示时间线上的一个点(瞬时)
  • LocalDate: 表示没有时区的日期, LocalDate是不可变并且线程安全的
  • LocalTime: 表示没有时区的时间, LocalTime是不可变并且线程安全的
  • LocalDateTime: 表示没有时区的日期时间, LocalDateTime是不可变并且线程安全的
  • Clock: 用于访问当前时刻、日期、时间,用到时区
  • Duration: 用秒和纳秒表示时间的数量(长短),用于计算两个日期的“时间”间隔
  • Period: 用于计算两个“日期”间隔

其中,LocalDate、LocalTime、LocalDateTime是新API里的基础对象,绝大多数操作都是围绕这几个对象来进行的,有必要搞清楚:

LocalDate : 只含年月日的日期对象
LocalTime :只含时分秒的时间对象
LocalDateTime : 同时含有年月日时分秒的日期对象

本文将以实例讲解日常开发中常用到的时间日期操作,如:

获取当前日期、时间
指定时间日期创建对应的对象
计算两个时间点的间隔
判断两个时间的前后
时间日期的格式化
获取时间戳
时间、日期相加减
获取给定时间点的年份、月份、周、星期等
……

新时间日期API详解与示例
获取当前时间
LocalDate localDate = LocalDate.now(); 
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now(); 
System.out.println(localDate); 
System.out.println(localTime);
System.out.println(localDateTime); 

运行结果:

2020-05-01
09:46:31.857
2020-05-01T09:46:31.857

根据指定日期/时间创建对象
LocalDate localDate = LocalDate.of(2018, 1, 13); 
LocalTime localTime = LocalTime.of(9, 43, 20);
LocalDateTime localDateTime = LocalDateTime.of(2018, 1, 13, 9, 43, 20); System.out.println(localDate); 
System.out.println(localTime);
System.out.println(localDateTime); 

运行结果:

2018-01-13
09:43:20
2018-01-13T09:43:20
日期时间的加减
  • 对于LocalDate,只有精度大于或等于日的加减,如年、月、日;
  • 对于LocalTime,只有精度小于或等于时的加减,如时、分、秒、纳秒;
  • 对于LocalDateTime,则可以进行任意精度的时间相加减;
 LocalDateTime localDateTime = LocalDateTime.now();
        //以下方法的参数都是long型,返回值都是LocalDateTime
        LocalDateTime plusYearsResult = localDateTime.plusYears(2L);
        LocalDateTime plusMonthsResult = localDateTime.plusMonths(3L);
        LocalDateTime plusDaysResult = localDateTime.plusDays(7L);
        LocalDateTime plusHoursResult = localDateTime.plusHours(2L);
        LocalDateTime plusMinutesResult = localDateTime.plusMinutes(10L);
        LocalDateTime plusSecondsResult = localDateTime.plusSeconds(10L);
        System.out.println("当前时间是 : " + localDateTime + "\n"
                + "当前时间加2年后为 : " + plusYearsResult + "\n"
                + "当前时间加3个月后为 : " + plusMonthsResult + "\n"
                + "当前时间加7日后为 : " + plusDaysResult + "\n"
                + "当前时间加2小时后为 : " + plusHoursResult + "\n"
                + "当前时间加10分钟后为 : " + plusMinutesResult + "\n"
                + "当前时间加10秒后为 : " + plusSecondsResult + "\n" );
        //也可以以另一种方式来相加减日期,即plus(long amountToAdd, TemporalUnit unit)
        // 参数1 : 相加的数量, 参数2 : 相加的单位
        LocalDateTime nextMonth = localDateTime.plus(1, ChronoUnit.MONTHS);
        LocalDateTime nextYear = localDateTime.plus(1, ChronoUnit.YEARS);
        LocalDateTime nextWeek = localDateTime.plus(1, ChronoUnit.WEEKS);
        System.out.println("now : " + localDateTime + "\n"
                + "nextYear : " + nextYear + "\n"
                + "nextMonth : " + nextMonth + "\n"
                + "nextWeek :" + nextWeek + "\n" );
        //日期的减法用法一样,在此不再举例

运行结果:

当前时间是 : 2020-05-01T09:50:08.522
当前时间加2年后为 : 2022-05-01T09:50:08.522
当前时间加3个月后为 : 2020-08-01T09:50:08.522
当前时间加7日后为 : 2020-05-08T09:50:08.522
当前时间加2小时后为 : 2020-05-01T11:50:08.522
当前时间加10分钟后为 : 2020-05-01T10:00:08.522
当前时间加10秒后为 : 2020-05-01T09:50:18.522

now : 2020-05-01T09:50:08.522
nextYear : 2021-05-01T09:50:08.522
nextMonth : 2020-06-01T09:50:08.522
nextWeek :2020-05-08T09:50:08.522
将年、月、日等修改为指定的值,并返回新的日期(时间)对象

析: 其效果与时间日期相加减差不多,如今天是2018-01-13,要想变为2018-01-20有两种方式
a. localDate.plusDays(20L) -> 相加指定的天数
b. localDate.withDayOfYear(20) -> 直接指定到哪一天

  LocalDate localDate = LocalDate.now();
        //当前时间基础上,指定本年当中的第几天,取值范围为1-365,366
        LocalDate withDayOfYearResult = localDate.withDayOfYear(200);
        //当前时间基础上,指定本月当中的第几天,取值范围为1-29,30,31
        LocalDate withDayOfMonthResult = localDate.withDayOfMonth(5);
        //当前时间基础上,直接指定年份
        LocalDate withYearResult = localDate.withYear(2017);
        //当前时间基础上,直接指定月份
        LocalDate withMonthResult = localDate.withMonth(5);
        System.out.println("当前时间是 : " + localDate + "\n"
                + "指定本年当中的第200天 : " + withDayOfYearResult + "\n"
                + "指定本月当中的第5天 : " + withDayOfMonthResult + "\n"
                + "直接指定年份为2017 : " + withYearResult + "\n"
                + "直接指定月份为5月 : " + withMonthResult + "\n" ); 

运行结果:

当前时间是 : 2020-05-01
指定本年当中的第200: 2020-07-18
指定本月当中的第5: 2020-05-05
直接指定年份为2017 : 2017-05-01
直接指定月份为5: 2020-05-01
获取日期的年月日周时分秒
   LocalDateTime localDateTime = LocalDateTime.now();
        int dayOfYear = localDateTime.getDayOfYear();
        int dayOfMonth = localDateTime.getDayOfMonth();
        DayOfWeek dayOfWeek = localDateTime.getDayOfWeek();
        System.out.println("今天是" + localDateTime + "\n"
                + "本年当中第" + dayOfYear + "天" + "\n"
                + "本月当中第" + dayOfMonth + "天" + "\n"
                + "本周中星期" + dayOfWeek.getValue() + "-即" + dayOfWeek + "\n");
        //获取当天时间的年月日时分秒
        int year = localDateTime.getYear();
        Month month = localDateTime.getMonth();
        int day = localDateTime.getDayOfMonth();
        int hour = localDateTime.getHour();
        int minute = localDateTime.getMinute();
        int second = localDateTime.getSecond();
        System.out.println("今天是" + localDateTime + "\n"
                + "年 : " + year + "\n"
                + "月 : " + month.getValue() + "-即 " + month + "\n"
                + "日 : " + day + "\n"
                + "时 : " + hour + "\n"
                + "分 : " + minute + "\n"
                + "秒 : " + second + "\n");

运行结果:

今天是2020-05-01T09:53:36.660
本年当中第122天
本月当中第1天
本周中星期5-即FRIDAY

今天是2020-05-01T09:53:36.660
年 : 2020
月 : 5-即 MAY
日 : 1
时 : 9
分 : 53
秒 : 36
时间日期前后的比较与判断
//判断两个时间点的前后
LocalDate localDate1 = LocalDate.of(2017, 8, 8);
LocalDate localDate2 = LocalDate.of(2018, 8, 8);
boolean date1IsBeforeDate2 = localDate1.isBefore(localDate2); System.out.println("date1IsBeforeDate2 : " + date1IsBeforeDate2); 
// date1IsBeforeDate2 == true 
判断是否为闰年
LocalDate now = LocalDate.now(); 
System.out.println("now : " + now + ", is leap year ? " + ); 
java8时钟 : clock()
//返回当前时间,根据系统时间和UTC
Clock clock = Clock.systemUTC(); 
// 运行结果: SystemClock[Z] 
System.out.println(clock); 
时间戳

事实上Instant就是java8以前的Date,
可以使用以下两个类中的方法在这两个类型之间进行转换,
比如Date.from(Instant)就是用来把Instant转换成java.util.date的,
而new Date().toInstant()就是将Date转换成Instant的

Instant instant = Instant.now();
        //2019-06-08T16:50:19.174Z 
        System.out.println(instant);
        Date date = Date.from(instant);
        Instant instant2 = date.toInstant(); 
        //Sun Jun 09 00:50:19 CST 2019 
        System.out.println(date); 
        //2019-06-08T16:50:19.174Z 
        System.out.println(instant2); 
计算时间、日期间隔

Duration:用于计算两个“时间”间隔
Period:用于计算两个“日期”间隔

   //计算两个日期的日期间隔-年月日
        LocalDate date1 = LocalDate.of(2018, 2, 13);
        LocalDate date2 = LocalDate.of(2017, 3, 12);
        //内部是用date2-date1,所以得到的结果是负数
        Period period = Period.between(date1, date2);
        System.out.println("相差年数 : " + period.getYears());
        System.out.println("相差月数 : " + period.getMonths());
        System.out.println("相差日数 : " + period.getDays());
        //还可以这样获取相差的年月日 
        System.out.println("-------------------------------");
        long years = period.get(ChronoUnit.YEARS);
        long months = period.get(ChronoUnit.MONTHS);
        long days = period.get(ChronoUnit.DAYS);
        System.out.println("相差的年月日分别为 : " + years + "," + months + "," + days);
        //注意,当获取两个日期的间隔时,并不是单纯的年月日对应的数字相加减,
        // 而是会先算出具体差多少天,在折算成相差几年几月几日的 
        // 计算两个时间的间隔
        System.out.println("-------------------------------");
        LocalDateTime date3 = LocalDateTime.now();
        LocalDateTime date4 = LocalDateTime.of(2018, 1, 13, 22, 30, 10);
        Duration duration = Duration.between(date3, date4);
        System.out.println(date3 + " 与 " + date4 + " 间隔 " + "\n" 
                + " 天 :" + duration.toDays() + "\n" + 
                " 时 :" + duration.toHours() + "\n" + 
                " 分 :" + duration.toMinutes() + "\n" 
                + " 毫秒 :" + duration.toMillis() + "\n"
                + " 纳秒 :" + duration.toNanos() + "\n");
        //注意,并没有获得秒差的,但既然可以获得毫秒,秒就可以自行获取了 

运行结果:

相差年数 : 0
相差月数 : -11
相差日数 : -1
-------------------------------
相差的年月日分别为 : 0,-11,-1
-------------------------------
2020-05-01T09:57:39.5952018-01-13T22:30:10 间隔 
 天 :-838:-20123:-1207407
 毫秒 :-72444449595
 纳秒 :-72444449595000000

当计算程序的运行时间时,应当使用时间戳Instant

   Instant ins1 = Instant.now();
        for (int i = 0; i < 10000000; i++) {
            //循环一百万次
        }
        Instant ins2 = Instant.now();
        Duration duration = Duration.between(ins1, ins2);
        System.out.println("程序运行耗时为 : " + duration.toMillis() + "毫秒");
时间日期的格式化(格式化后返回的类型是String)
1. 使用jdk自身配置好的日期格式
  //使用jdk自身配置好的日期格式
        DateTimeFormatter formatter1 = DateTimeFormatter.ISO_DATE_TIME;
        LocalDateTime date1 = LocalDateTime.now();
        //反过来调用也可以 :
        date1.format(formatter1);
        String date1Str = formatter1.format(date1);
        System.out.println(date1Str);

运行结果:


2020-05-01T09:59:34.632
2. 使用自定义格式
LocalDateTime date1 = LocalDateTime.now(); 
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); String date2Str = formatter2.format(date1); 
System.out.println(date2Str); 

运行结果:

2020050110:02:30

注:自定义转化的格式一定要与日期类型对应

  • LocalDate只能设置仅含年月日的格式
  • LocalTime只能设置仅含时分秒的格式
  • LocalDateTime可以设置含年月日时分秒的格式

代码如下:

DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd"); System.out.println(formatter3.format(LocalDate.now()));
System.out.println("-------------------------------");
DateTimeFormatter formatter4 = DateTimeFormatter.ofPattern("HH:mm:ss"); System.out.println(formatter4.format(LocalTime.now())); 

运行结果:

2020-05-01
-------------------------------
10:03:10

img

​ image.png

将时间字符串形式转化为日期对象
String datetime =  "2018-01-13 21:27:30";  
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); LocalDateTime ldt = LocalDateTime.parse(datetime, dtf); System.out.println(ldt); 

运行结果:

2018-01-13T21:27:30

img

​ image.png

注:格式的写法必须与字符串的形式一样
2018-01-13 21:27:30 对应 yyyy-MM-dd HH:mm:ss
20180113213328 对应 yyyyMMddHHmmss
否则会报运行时异常!

但要记住:得到的最终结果都是类似2018-01-13T21:27:30的格式

因为在输出LocalDateTime对象时,会调用其重写的toString方法。

@Override
    public String toString() { return date.toString() + 'T' + time.toString(); } 
将时间日期对象转为格式化后的时间日期对象
//新的格式化API中,格式化后的结果都默认是String,有时我们也需要返回经过格式化的同类型对象
LocalDateTime ldt1 = LocalDateTime.now(); DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String temp = dtf1.format(ldt1); LocalDateTime formatedDateTime = LocalDateTime.parse(temp, dtf1); System.out.println(formatedDateTime); 

运行结果:

2020-05-01T10:04:29

​ image.png

long毫秒值转换为日期
System.out.println("---------long毫秒值转换为日期---------");
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String longToDateTime = df.format(
                LocalDateTime.ofInstant(
                        Instant.ofEpochMilli(System.currentTimeMillis()), 
                        ZoneId.of("Asia/Shanghai")));
        System.out.println(longToDateTime);

运行结果:

---------long毫秒值转换为日期---------
2020-05-01 10:05:58

重复注解与类型注解

示例

@Repeatable(MyAnnotations.class)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE_PARAMETER})
/**
 * TYPE, 类
 * FIELD, 属性
 * METHOD, 方法
 * PARAMETER, 参数
 * CONSTRUCTOR, 构造器
 * LOCAL_VARIABLE,方法内部的局部变量
 * TYPE_PARAMETER, 类型注解
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "hello";
}

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

    @MyAnnotation("hello")
    @MyAnnotation("world")
    public void show(@MyAnnotation("aaa") String str) {
    }

    @Test
    public void test() throws Exception {
        Class<TestAnnotation> clazz = TestAnnotation.class;
        Method method = clazz.getDeclaredMethod("show");
        MyAnnotation[] annotationsByType = method.getAnnotationsByType(MyAnnotation.class);
        for (MyAnnotation myAnnotation : annotationsByType) {
            System.out.println(myAnnotation.value());
        }
    }
}

Pattern(“yyyy-MM-dd HH:mm:ss”);
String longToDateTime = df.format(
LocalDateTime.ofInstant(
Instant.ofEpochMilli(System.currentTimeMillis()),
ZoneId.of(“Asia/Shanghai”)));
System.out.println(longToDateTime);


运行结果:

```java
---------long毫秒值转换为日期---------
2020-05-01 10:05:58

重复注解与类型注解

示例

@Repeatable(MyAnnotations.class)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE_PARAMETER})
/**
 * TYPE, 类
 * FIELD, 属性
 * METHOD, 方法
 * PARAMETER, 参数
 * CONSTRUCTOR, 构造器
 * LOCAL_VARIABLE,方法内部的局部变量
 * TYPE_PARAMETER, 类型注解
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "hello";
}

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

    @MyAnnotation("hello")
    @MyAnnotation("world")
    public void show(@MyAnnotation("aaa") String str) {
    }

    @Test
    public void test() throws Exception {
        Class<TestAnnotation> clazz = TestAnnotation.class;
        Method method = clazz.getDeclaredMethod("show");
        MyAnnotation[] annotationsByType = method.getAnnotationsByType(MyAnnotation.class);
        for (MyAnnotation myAnnotation : annotationsByType) {
            System.out.println(myAnnotation.value());
        }
    }
}
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐