天天看点

Java8 新特性Java8新特性

Java8新特性

推荐阅读:
  • CSDN-云深不知处:Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合
  • CSDN-高可用架构:你可能未曾使用的新 Java 特性
  • lambda表达式、方法引用、构造器引用、数组引用
  • Stream流
  • optional
  • 时间日期API [线程安全]
  • 重复注解与类型注解
  • ForkJoin框架
  • 接口的默认方法和静态方法

1. Lambda表达式

Java8中引入了一个新的操作符 “->” 该操作符称位箭头操作符
  • 定义一个接口(函数式接口)
@FunctionalInterface
public interface MyFunction {

    Integer getValue(Integer num);
}
           
  • 使用lambda表达式
public class LambdaTest2 {

    public Integer operation(Integer num, MyFunction mf) {
        return mf.getValue(num);
    }

    @Test
    public void test1() {
        Integer num = operation(100, (x) -> x * x);
        System.out.println(num);

        Integer num2 = operation(100, (x) -> x + 200);
        System.out.println(num2);
    }

}
           

1.1 内置四大核心函数式接口

接口名称 接口类型 方法 方法参数 方法返回值类型
Consumer 消费型 void accept(T t) ×
Supplier 供给型 T get() ×
Function<T,R> 函数型 R apply (T t)
Predicate 断言型 boolean test(T t)

消费型接口

/**
     * Consumer<T>: 消费型接口
     * 需求:
     */
    public void happy(double money, Consumer<Double> con) {
        con.accept(money);
    }

    @Test
    public void happyTest() {
        happy(10000, m -> System.out.println("今天吃饭花了" + m));
    }
           

供给型接口

/**
     * Supplier<T>:供给型接口
     * 需求:随机存储10个数字存入集合
     */
    public List<Integer> getNumList(int num, Supplier<Integer> sup) {
        List<Integer> list = new ArrayList<>();

        for (int i = 0; i < num; i++) {
            Integer n = sup.get();
            list.add(n);
        }
        System.out.println(list);
        return list;
    }

    @Test
    public void supTest() {
        getNumList(10, () -> (int) (Math.random() * 100));
    }
           

函数型接口

/**
     * Function<T,R>: 函数型接口
     */
    public String strHandler(String str, Function<String, String> fun) {
        return fun.apply(str);
    }

  
    @Test
    public void funTest() {
        String s = strHandler("\t\t\t 你是谁啊! \t\t\t", (str) -> str.trim());
        System.out.println(s);
    }
           

断言型接口

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

    @Test
    public void preHandler(){
        List<String> stringList = Arrays.asList("Hello", "Friends", "Lambda", "www", "ok");
        List<String> strings = filterStr(stringList, (str) -> str.length() > 3);
        System.out.println(strings);
    }
           

1.2 方法引用

若Lambda体中的内容有方法已经实现了,我们可以使用“方法引用”(可理解为是lambda表达式的另外一种表现形式)

有三种语法形式

  • 对象::实例方法名
  • 类名::静态方法名
  • 类名::实例方法名

注意

  1. Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
  2. 若Lambda参数列表中的第一参数是实例方法的调用者,而第二个参数是实例方法的参数时,可使用 ClassName::methodName

对象::实例方法名

/**
     * 对象::实例方法名
     */
    @Test
    public void test1() {
        PrintStream ps = System.out;
        // 方法引用
        Consumer<String> con = ps::println;
        con.accept("aqwsdasagas");
    }
           

类名::静态方法名

/**
     * 类::静态方法名
     */
    @Test
    public void test2() {
        // lambda
        Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
        // 方法引用
        Comparator<Integer> com1 = Integer::compare;
    }
           

类::实例方法名

/**
     * 类::实例方法名
     */
    @Test
    public void test3() {
        BiPredicate<String, String> bp = (x, y) -> x.equals(y);
        BiPredicate<String, String> bp1 = String::equals;
    }

 @Test
    public void test2() {
        Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
        Comparator<Integer> com1 = Integer::compare;
    }
           

1.3 构造器引用

注意:需要调用的构造函数的参数列表要与函数式接口中抽象方法的参数列表保持一致!
前提定义Employee和枚举类
  • Employee
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {

    private int id;
    private String name;
    private int age;
    private double salary;
    private Status status;

    public Employee(int id) {
        this.id = id;
    }
}
           
  • 构造器引用案例
/**
     * 构造器引用: 获得了一个无参构造
     */
    @Test
    public void test4() {
        // lambda
        Supplier<Employee> sup = () -> new Employee();
        // 构造器引用
        Supplier<Employee> sup2 = Employee::new;
        // 应该获得了一个无参构造,因为get方法就是无参有返回值
        Employee employee = sup2.get();
        System.out.println(employee);
    }

    /**
     * 构造器引用:获取参数为int id的构造方法
     */
    @Test
    public void test5() {
        Function<Integer, Employee> fun = (x) -> new Employee(x);
        Employee apply = fun.apply(2);
        Function<Integer, Employee> fun2 = Employee::new;
        // 根据apply的参数个数去调用对应的构造方法(一个参数,int类型)
        Employee apply2 = fun.apply(3);
        System.out.println(apply);
        System.out.println(apply2);
    }
           

1.4 数组引用

Type::new
/**
     * 数组引用
     */
    @Test
    public void test7(){
        Function<Integer,String[]> fun = (x) -> new String[x];
        String[] strs = fun.apply(10);
        System.out.println(strs.length);

        Function<Integer,String[]> fun2 = String[]::new;
        System.out.println(fun2.apply(15).length);
    }
           

1.5 情景处理

某公司员工定制排序

package cn.luis.lambda.practise;

import cn.luis.lambda.method.Employee;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Compony {

    /**
     * 某公司员工基本信息
     */
    List<Employee> emps = Arrays.asList(
            new Employee(1,"zhanagsan", 18, 1111.11),
            new Employee(2,"lisi", 49, 2222.22),
            new Employee(3,"zhaoliu", 49, 3333.33),
            new Employee(4,"tianqi", 35, 4444.44)
    );


    /**
     * 定制排序:先按年龄比,年龄相同按姓名比
     */
    @Test
    public void test1() {
        Collections.sort(emps, (e1, e2) -> {
            if (e1.getAge() == e2.getAge()) {
                return e1.getName().compareTo(e2.getName());
            } else {
                return Integer.compare(e1.getAge(),e2.getAge());
            }
        });

        System.out.println("======================");

        for (Employee emp : emps) {
            System.out.println(emp);
        }

    }

}

           

字符串处理

package cn.luis.lambda.practise;

import org.junit.jupiter.api.Test;

public class StringTest {
    /**
     * 函数式接口
     */
    interface StringFun {
        String getValue(String str);
    }

    /**
     * 用于处理字符串
     */
    public String strHandler(String str, StringFun sf) {
        return sf.getValue(str);
    }


    /**
     * 字符串处理
     */
    @Test
    public void test1() {
        String s1 = strHandler("\t\t\t hello , may i have a dog \t\t\t", str -> str.trim());
        System.out.println(s1);
        String s2 = strHandler("asdfghjkl", str -> str.toUpperCase());
        System.out.println(s2);
        String s3 = strHandler("asdfghjkl", str -> str.substring(0, 3));
        System.out.println(s3);
    }

}

           

long型数字处理

package cn.luis.lambda.practise;

import org.junit.jupiter.api.Test;

public class LongTest {

    /**
     * 函数式接口
     */
    public interface MyFunction<T, R> {

        R getValue(T t1, T t2);

    }

    /**
     * 对于两个long型数据进行处理
     * MyFunction<Integer,Integer>: 传入参数类型、返回值类型
     */
    public void operacte(Integer x, Integer y, MyFunction<Integer, Integer> mf) {
        System.out.println(mf.getValue(x, y));
    }

    @Test
    public void test1() {
        operacte(100, 100, (x, y) -> x + y);
    }
}

           

2. Stream流

Stream的三个操作步骤

  • 创建Stream
  • 中间操作
  • 终止操作(终端操作)

2.1 创建Stream

创建stream的四种方式

  • 通过Collection系列集合提供的

    stream()

    方法或

    parallelStream()

List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();
		Stream<String> stream2 = list.parallelStream();
           
  • 通过静态方法获取一个数组流
Employee[] employees = new Employee[10];
        Stream<Employee> stream2 = Arrays.stream(employees);
           
  • 通过Stream类静态类中的of方法
  • 创建无限流:两种(迭代和生成)
// 4.1迭代 : 传一个起始值和一个一元运算
        Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
        /// 中间操作  终止操作
        stream4.limit(10).forEach(System.out::println);

        // 4.2生成(无限生成随机数)
        Stream.generate(()->Math.random()).forEach(System.out::println);
           

2.2 中间操作

中间操作:不会执行任何操作,终止操作:一次性执行全部内容,即“惰性求值”

2.2.1 筛选与切片

方法 描述
filter 接收Lambda,从流中排除某些元素
limit 截断流,使其元素不超过给定数量
skip(n) 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
distinct 筛选,通过流所生成元素的hashCode() 和 equals() 去除重复元素

例子:

  • filter
List<Employee> employees = Arrays.asList(
            new Employee(1, "张三", 18, 1111.11),
            new Employee(2, "李四", 49, 2222.22),
            new Employee(3, "赵六", 57, 3333.33),
            new Employee(4, "田七", 35, 4444.44),
            new Employee(4, "田七", 35, 4444.44)
    );	

    /**
     * 中间操作:不会执行任何操作,终止操作:一次性执行全部内容,即“惰性求值”
     * 1.筛选与切片:filter
     */
	@Test
    public void test1() {
        // 内部迭代:迭代操作由Stream API完成
        employees.stream()
                .filter(e -> e.getAge() > 35)
                .forEach(System.out::println);

        // 外部迭代
        Iterator<Employee> it = employees.iterator();

        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
           
  • limit
package cn.luis.stream.intermediate;

import cn.luis.stream.Employee;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class LimitTest {

    List<Employee> employees = Arrays.asList(
            new Employee(1, "张三", 18, 1111.11),
            new Employee(3, "赵六", 57, 3333.33),
            new Employee(4, "田七", 35, 4444.44),
            new Employee(2, "李四", 49, 2222.22),
            new Employee(5, "田七", 35, 4444.44)
    );

    /**
     * 中间操作:不会执行任何操作,终止操作:一次性执行全部内容,即“惰性求值”
     * 2.筛选与切片:limit
     */
    @Test
    public void test2() {
        // limit;短路,找到两个符合的就终止了
        employees.stream()
                .filter(e -> {
                    //System.out.println("不符合条件的或者直接抛弃了!" + e);
                    return e.getAge() > 20;
                })
                .limit(2)
                .forEach(System.out::println);
    }

}
           
  • skip
/**
     * 中间操作:不会执行任何操作,终止操作:一次性执行全部内容,即“惰性求值”
     * 1.筛选与切片:skip
     */
    @Test
    public void test3() {
        // limit;跳过前两个
        employees.stream()
                .filter(e -> {
                    System.out.println("呦!");
                    return e.getAge() > 25;
                })
                .skip(2)
                .forEach(System.out::println);
    }
           
  • distinct
/**
     * 中间操作:不会执行任何操作,终止操作:一次性执行全部内容,即“惰性求值”
     * 1.筛选与切片:distinct
     */
    @Test
    public void test4() {
        // distinct;去重 【employees要重写hashcode和equals】
        employees.stream()
                .filter(e -> {
                    System.out.println("呦!");
                    return e.getAge() > 25;
                })
                .distinct()
                .forEach(System.out::println);
    }
           

2.2.2 映射

map里传入Function函数型接口,传入一个参数返回一个值
方法 描述
map 接收lambda,将元素转换成其他形式或提取信息,接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap 接收一个函数作为参数,将该流中的每一个值都换成另一个流,然后把所有流连成一个流

例子:

public class StreamApi3 {

    List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");

    @Test
    public void test1() {
        list.stream()
                .map(s -> s.toUpperCase())
                .forEach(System.out::println);

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


        // 这种套娃式可以用flatMap替代 (见test2)
        Stream<Stream<Character>> stream = list.stream().map(StreamApi3::filterCharacter);
        stream.forEach(sm->{
            sm.forEach(System.out::println);
        });
    }


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

    @Test
    public void test2() {
        // 扁平化maop:原来把流放入map流,现在是直接将流中的元素放入flatmap流中
        // 把{a,a,a},{b,b,b} ... 转换成了 {a,a,a,b,b,b,c,c,c}
        Stream<Character> characterStream = list.stream()
            .flatMap(StreamApi3::filterCharacter);
        
        characterStream.forEach(System.out::println);
    }
}
           

2.2.3 排序

方法 描述
sorted() 自然排序(Comparable)
sorted(Comparator com) 定制排序(Comparator)

例子:

/**
     * 中间操作:不会执行任何操作,终止操作:一次性执行全部内容,即“惰性求值”
     * 3. 排序
     * sorted() -- 自然排序(Comparable)
     * sorted(Comparator com) -- 定制排序(Comparator)
     */
    @Test
    public void test1() {

        // 自然排序
        Stream<Employee> sorted = employees.stream().sorted();
        // 按年龄排序,年龄相同按名字排序
        employees.stream()
                .sorted((e1, e2) -> {
                    if (e1.getAge() == e2.getAge()) {
                        return e1.getName().compareTo(e2.getName());
                    } else {
                        Integer x = e1.getAge();
                        return x.compareTo(e2.getAge());
                    }
                }).forEach(System.out::println);
    }
           

2.3 终止操作

2.3.1 查找与匹配

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

例子:

  • 查找的对象
List<Employee> employees = Arrays.asList(
            new Employee(1, "张三", 18, 1111.11, Status.BUSY),
            new Employee(2, "李四", 49, 2222.22, Status.FREE),
            new Employee(3, "赵六", 57, 3333.33, Status.BUSY),
            new Employee(4, "田七", 35, 4444.44, Status.VOCATION),
            new Employee(2, "李十四", 49, 2222.22, Status.FREE),
            new Employee(2, "李十四", 49, 2222.22, Status.FREE),
            new Employee(2, "李十四", 49, 2222.22, Status.FREE)
    );
           
  • 查找与匹配
@Test
    public void test1() {
        boolean b = employees.stream()
                .allMatch(e -> e.getStatus().equals(Status.BUSY));
        System.out.println(b);

        boolean b2 = employees.stream()
                .anyMatch(e -> e.getStatus().equals(Status.BUSY));
        System.out.println(b2);

        boolean b3 = employees.stream()
                .noneMatch(e -> e.getStatus().equals(Status.BUSY));
        System.out.println(b3);

        Optional<Employee> firste = employees.stream()
                .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
                .findFirst();
        System.out.println(firste.get());

        Optional<Employee> anye = employees.stream()
                .filter(e -> e.getStatus().equals(Status.FREE))
                .findAny();
        System.out.println(anye.get());

        // 并行
        Optional<Employee> anye2 = employees.parallelStream()
                .filter(e -> e.getStatus().equals(Status.FREE))
                .findAny();
        System.out.println(anye2.get());
    }
           
  • 查找与匹配
@Test
    public void test2() {
        long count = employees.stream()
                .count();
        System.out.println(count);

        Optional<Employee> maxe = employees.stream()
                .max((e1, e2) -> Double.compare(e1.getAge(), e2.getAge()));
        System.out.println(maxe.get());

        Optional<Double> min = employees.stream()
                .map(Employee::getSalary)
                .min(Double::compare);
        System.out.println(min.get());
    }
           

2.3.2 规约

方法 描述
reduce 可以将流中的元素反复结合起来,得到一个值

例子:

/**
     * reduce(起始值,二元运算): 规约
     */
    @Test
    public void test3() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        // 先把起始值0作为x,再从list取数放入y
        Integer sum = list.stream()
                .reduce(0, (x, y) -> x + y);
        System.out.println(sum);

        System.out.println("----------计算公司总工资---------");
        Double reduce = employees.stream()
                .map(e -> e.getSalary())
                .reduce(0.0, (x, y) -> x + y);
        // 优化写法(map-reduce模式)
        Optional<Double> reduce1 = employees.stream()
                .map(Employee::getSalary)
                .reduce(Double::sum);
        System.out.println(reduce1.get());
    }
           

2.3.3 收集

包括集合、计算、分组、分区
方法 描述
collect 将流转换为其他形式,接收一个Collectot接口的实现,用于给Stream中的元素汇总的方法
  • 集合
@Test
    public void test4() {
        List<String> nameList = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());
        nameList.forEach(System.out::println);
        System.out.println("-----------过滤重复数据--------------");
        Set<String> nameSet = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toSet());
        nameSet.forEach(System.out::println);
        System.out.println("-----------放到其他类型集合--------------");
        HashSet<String> nameHashSet = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toCollection(HashSet::new));
        nameHashSet.forEach(System.out::println);
    }
           
  • 计算
/**
     * collect 之 计算
     */
    @Test
    public void test5() {
        // 总数
        Long collect = employees.stream()
                .collect(Collectors.counting());
        System.out.println(collect);

        // 平均值
        Double avg = employees.stream()
                .collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(avg);

        // 总和
        Double sum = employees.stream()
                .collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(sum);

        // 最大值
        Optional<Employee> maxo = employees.stream()
                .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
        System.out.println(maxo.get());

        // 最小值
        Optional<Double> mino = employees.stream()
                .map(Employee::getSalary)
                .collect(Collectors.minBy(Double::compare));
        System.out.println(mino.get());
    }
           
  • 分组
/**
     * collect 之 分组
     */
    @Test
    public void test6() {
        // 分组
        Map<Status, List<Employee>> statusListMap = employees.stream()
                .collect(Collectors.groupingBy(Employee::getStatus));
        /*statusListMap.keySet().forEach(System.out::println);
        statusListMap.entrySet().forEach(System.out::println);
        System.out.println(statusListMap);*/

        // 多级分组
        /*employees.stream()
                .collect(Collectors.groupingBy(Employee::getStatus), Collectors.groupingBy((Employee e) -> {
                    if (e.getAge() <= 35) {
                        return "青年";
                    }else if (e.getAge() > 20) {
                        return "老年";
                    } else {
                        return "少年";
                    }
                }));*/

        // 分区: 满足条件的分一个区,不满足的分一个区
        Map<Boolean, List<Employee>> collect = employees.stream()
                .collect(Collectors.partitioningBy(e -> e.getSalary() > 2000.0));
        System.out.println(collect);

        // Double:强大计算
        DoubleSummaryStatistics aDouble = employees.stream()
                .collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(aDouble.getAverage());
        System.out.println(aDouble.getMax());
        System.out.println(aDouble.getCount());

        // 字符串拼接
        String str = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(","));
        System.out.println(str);
    }
           

2.4 应用

  • 交易员类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Trander {

    private String name;
    private String city;

}
           
  • 交易类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Transaction {

    private Trander trander;
    private int year;
    private int value;
}
           
  • 练习
package cn.luis.stream.practise;

import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Luis
 * @version 1.0
 * @description TODO
 * @date 2020/12/8 9:25
 */
public class TransactionTest {

    List<Transaction> transactions;

    @Before
    public void before() {
        Trander raoul = new Trander("Raoul", "Cambridge");
        Trander mario = new Trander("Mario", "Milan");
        Trander alan = new Trander("Raoul", "Cambridge");
        Trander brian = new Trander("Brian", "Cambridge");

        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)
        );
    }

    /**
     * 找到2011年发生的所有交易,并按交易额排序(从低到高)
     * [有条件先筛选,排序记得写规则]
     */
    @Test
    public void test1() {
        transactions.stream()
                .filter(t -> t.getYear() == 2011)
                .sorted((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()))
                .forEach(System.out::println);
    }

    /**
     * 交易员都在哪些不同的城市工作过
     * [记得去重]
     */
    @Test
    public void test2() {
        transactions.stream()
                .map(t -> t.getTrander().getCity())
                .distinct()
                .forEach(System.out::println);
    }

    /**
     * 查找所有来自剑桥的交易员,并按姓名排序
     */
    @Test
    public void test3() {
        transactions.stream()
                .map(t -> t.getTrander())
                .distinct()
                .filter(trander -> "Cambridge".equals(trander.getCity()))
                .sorted((t1, t2) -> t1.getName().compareTo(t2.getName()))
                .forEach(System.out::println);

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

        transactions.stream()
                .map(t -> t.getTrander().getName())
                .sorted()
                .forEach(System.out::println);

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

        String reduceStr = transactions.stream()
                .map(t -> t.getTrander().getName())
                .sorted()
                .reduce("", String::concat);
        System.out.println(reduceStr);

        System.out.println("---------------------------------");
    // 将所有姓名组成字符串,然后按照子母排排序
        transactions.stream()
                .map(t->t.getTrander().getName())
                .flatMap(TransactionTest::filterCharacter)
                .sorted()
                .forEach(System.out::print);
    }

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

    /**
     * 返回所有交易员的姓名字符串,按字母顺序排序
     */
    @Test
    public void test4() {
        String collect = transactions.stream()
                .map(Transaction::getTrander)
                .map(Trander::getName)
                .sorted(String::compareToIgnoreCase)
                .collect(Collectors.joining(","));
        System.out.println(collect);
    }

    /**
     * 有没有交易员是在米兰工作的
     */
    @Test
    public void test5() {
        boolean b = transactions.stream()
                .map(Transaction::getTrander)
                .map(Trander::getCity)
                .noneMatch(c -> "Milan".equals(c));

        System.out.println(!b);

    }

    /**
     * 打印生活在剑桥的交易员的所有交易额
     */
    @Test
    public void test6() {
        Optional<Integer> reduce = transactions.stream()
                .filter(t -> "Cambridge".equals(t.getTrander().getCity()))
                .map(Transaction::getValue)
                .reduce(Integer::sum);
        System.out.println(reduce.isPresent());
        System.out.println(reduce.get());
    }

    /**
     * 所有交易中,最高的交易额
     */
    @Test
    public void test7() {
        Optional<Integer> max = transactions.stream()
                .map(Transaction::getValue)
                .max(Double::compare);
        System.out.println(max.get());

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

        Optional<Integer> collect = transactions.stream()
                .map(Transaction::getValue)
                .collect(Collectors.maxBy((v1, v2) -> v1.compareTo(v2)));
        System.out.println(collect.get());
    }
}
           

3. DateAPI

3.1 人读的时间

api 描述
LocalDate 日期 年月日
LocalTime 时间 时分秒
LocalDateTime 时间和日期 年月日+时分秒

例子:

@Test
    public void test1() {
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);

        LocalDateTime ldt2 = LocalDateTime.of(2020, 12, 9, 12, 56);
        System.out.println(ldt2);

        LocalDateTime ldt3 = ldt.plusYears(2);
        System.out.println(ldt3);

        LocalDateTime ldt4 = ldt.minusMonths(2);
        System.out.println(ldt4);

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

        int year = ldt.getYear();
        int monthValue = ldt.getMonthValue();
        int dayOfMonth = ldt.getDayOfMonth();
        int hour = ldt.getHour();
        int minute = ldt.getMinute();
        int second = ldt.getSecond();
        System.out.println(year);
        System.out.println(monthValue);
        System.out.println(dayOfMonth);
        System.out.println(hour);
        System.out.println(minute);
        System.out.println(second);

    }
           

结果:

2020-12-10T17:05:55.025
2020-12-09T12:56
2022-12-10T17:05:55.025
2020-10-10T17:05:55.025
----------------------------
2020
12
10
17
5
55
           

3.2 机器读的时间

方法 描述
Instant 时间戳(以unix元年:1970年1月1日00:00:00到某个时间之间的毫秒值)
Duration 计算两个“时间”之间的间隔
Period 计算两个“日期”之间的间隔

例子:

@Test
    public void test2() {
        // 默认获取UTC时区(格林威治)的时间
        Instant in1 = Instant.now();
        System.out.println(in1);

        // 加8小时
        OffsetDateTime odt = in1.atOffset(ZoneOffset.ofHours(8));
        System.out.println(odt);

        // 毫秒值
        long haomiao = in1.toEpochMilli();
        System.out.println(haomiao);

        Instant instant = Instant.ofEpochSecond(1000);
        
    }	
           
  • 计算时间间隔
/**
     * Duration:计算;两个“时间”之间的间隔
     */
    @Test
    public void test3() throws InterruptedException {
        Instant in1 = Instant.now();
        Thread.sleep(3000);
        Instant in2 = Instant.now();
        Duration duration = Duration.between(in1, in2);
        // 获取秒和纳秒用get~(),获取毫秒用to~()
        System.out.println(duration.toMillis()); // 3001

        LocalTime lt = LocalTime.now();
        Thread.sleep(2000);
        LocalTime lt2 = LocalTime.now();
        System.out.println(Duration.between(lt, lt2).toMillis()); // 2001
        System.out.println(Duration.between(lt2, lt).toMillis()); // -2001

    }
           
  • 计算日期间隔
/**
     * Period:计算;两个“日期”之间的间隔
     */
    @Test
    public void test4() throws InterruptedException {
        LocalDate ld1 = LocalDate.now();
        LocalDate ld2 = LocalDate.of(2015, 1, 1);
        Period period = Period.between(ld2, ld1);
        System.out.println(period);
        System.out.println(period.getYears());
        System.out.println(period.getMonths());
        System.out.println(period.getDays());
        
        /**
            P5Y11M9D
            5
            11
            9
        */

    }
           

3.3 时间校正器

api 描述
TemporalAdjuster 时间校正器
TemporalAdjusters 时间校正器工具类

例子:

@Test
    public void test5() {
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);
        // 将月中的天指定为10
        LocalDateTime ldt2 = ldt.withDayOfMonth(10);
        System.out.println(ldt2);

        // 下一个周日
        LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
        System.out.println(ldt3);

        // 自定义:下一个工作日是什么时候
        LocalDateTime gzr = ldt.with(l -> {
            LocalDateTime ldt4 = (LocalDateTime) l;
            DayOfWeek dayOfWeek = ldt4.getDayOfWeek();
            if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {
                return ldt4.plusDays(4);
            } else if (dayOfWeek.equals(DayOfWeek.SATURDAY)) {
                return ldt4.plusDays(2);
            } else {
                return ldt4.plusDays(1);
            }
        });
        System.out.println(gzr);
        System.out.println("工作日: " + gzr.getMonthValue() + "月" + gzr.getDayOfMonth() + "日");
    }
           

3.4 格式化时间、日期

api

api 描述
DateTimeFormatter 时间格式化

方法

api 描述 举例
.ofPattern() 定义日期转换格式 DateTimeFormatter.ofPattern(“yyyy-MM-dd HH:mm:ss”);

例子:

@Test
    public void test6() {

        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);

        // DateTimeFormatter、LocalDateTime 都有format方法
        DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // String strDate1 = dtf2.format(ldt);
        String strDate2 = ldt.format(dtf2);
        System.out.println(strDate2);
        LocalDateTime ldt2 = LocalDateTime.parse(strDate2, dtf2);
        System.out.println(ldt2);

    }
           

3.5 时区的处理

api 描述
ZoneId 时区
ZonedDate 时间
ZonedTime 日期
ZonedDateTime 日期和时间

例子:

/**
     * 时区的处理:ZonedDate、ZonedTime、ZonedDateTime
     */
    @Test
    public void test7() {
        Set<String> zoneIds = ZoneId.getAvailableZoneIds();
        //zoneIds.forEach(System.out::println);
        //System.out.println(zoneIds.stream().count()); // 599个时区

        // 指定时区
        LocalDateTime now1 = LocalDateTime.now();
        // 两种指定时区方法
        LocalDateTime now2 = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        // 两种指定时区方法
        ZonedDateTime zdt = now1.atZone(ZoneId.of("Asia/Shanghai"));
        System.out.println(now1);
        System.out.println(now2);
        System.out.println(zdt);
    }
           

3.6 Instant、LocalDateTime、Date间的转换

3.6.1 Date to LocalDateTime

/**
     * Date to LocalDateTime
     */
    @Test
    public void test9() {
        Date todayDate = new Date();
        System.out.println("todayDate = " + todayDate);

        LocalDateTime todayLocalDateTime = todayDate.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();

        System.out.println("todayLocalDateTime = " + todayLocalDateTime);
    }
           

结果:

todayDate = Fri Dec 11 10:26:00 CST 2020
todayLocalDateTime = 2020-12-11T10:26:00.115
           

3.6.2 LocalDateTime to Date

/**
     * LocalDateTime to Date
     */
    @Test
    public void test10() {
        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println("localDateTime = " + localDateTime);

        Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());

        System.out.println("date = " + date);
    }
           

结果:

localDateTime = 2020-12-11T10:29:17.403
date = Fri Dec 11 10:29:17 CST 2020
           

3.6.3 综合

通过:ZonedDateTime
/**
     * Instant、LocalDateTime、Date转换
     */
    @Test
    public void test8() {
        ZoneId zoneId = ZoneId.of("Asia/Shanghai");

        Instant instant = Instant.now();
        ZonedDateTime zonedDateTime = instant.atZone(zoneId);

        System.out.println("instant=== " + instant);
        System.out.println("zonedDateTime=== " + zonedDateTime);

        LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
        System.out.println("localDateTime=== " + localDateTime);

        Date date = Date.from(instant);
        System.out.println("date=== " + date);
    }
           

结果:

instant=== 2020-12-11T02:21:52.939Z
zonedDateTime=== 2020-12-11T10:21:52.939+08:00[Asia/Shanghai]
localDateTime=== 2020-12-11T10:21:52.939
date=== Fri Dec 11 10:21:52 CST 2020
           

3.6.4 DateUtil

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
 
public class Dateutili {
 
    public static Date asDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }
 
    public static Date asDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }
 
    public static LocalDate asLocalDate(Date date) {
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
    }
 
    public static LocalDateTime asLocalDateTime(Date date) {
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
    }
}
           

4. Optional

4.1 API

api Optional
位置 java.util.Optional
描述 是一个容器类,是一个可以为null的容器对象

4.2 方法

4.2.1 创建Optional类对象的方法

方法 描述
Optional.of(T t) 创建一个Optional实例
Optional.empty() 创建一个空的Optional实例
Optional.ofNullable(T t) 若t不为null,创建Optional实例,否则创建空实例

例子:

Optional.of(T t) 创建一个Optional实例
/**
     * Optional.of(T t) 创建一个Optional实例
     */
    @Test
    public void test1() {
        Girl girl = new Girl();
        girl = null;
        // of(T t): 保证t是非空的
        Optional<Girl> girl1 = Optional.of(girl);
        System.out.println(girl1);
    }
           
Optional.ofNullable(T t) 若t不为null,创建Optional实例,否则创建空实例
@Test
    public void test2() {
        Girl girl = new Girl();
        girl = null;
        // ofNullable(T t): t可以为null
        Optional<Girl> girl1 = Optional.ofNullable(girl);
        System.out.println(girl1);
    }
           

4.2.2 判断Optional容器中是否包含对象

方法 描述
isPresent() 判断是否包含对象
void idPresent(Consumer<? super ?> consumer) 如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它

4.2.3 获取Optional容器的对象

方法 描述
T get() 如果调用对象包含值,返回该值,否则抛异常
orElse(T other) 如果调用对象包含值,返回该值,否则返回 other对象
orElseGet(Supplier<? extends ?> s) 果调用对象包含值,返回该值,否则返回 s 获取的值
T orElseThrow(Supplier<? extends X> exceptionSupplier) 如果有值将其返回,否则抛出由Supplier接口实现提供的异常
map(Function f) 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
flatMap(Function mapper) 与map类似,要求返回值必须是Optional

4.3 情景应用

定义一个boy类、girl类
  • boy
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Boy {

    private Girl girl;
}
           
  • girl
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Girl {

    private String name;
}
           
测试类
如果boy为null,则空指针会在本类抛出,若name为空,则在getGirlName方法抛出

原始写法

package cn.luis.lambda.atguigu.optional;

import org.junit.Test;

import java.util.Optional;

public class OptionalTest {

    public String getGirlName(Boy boy) {
        return boy.getGirl().getName();
    }

    /**
     * 练习
     * 如果boy为null,则空指针会在本类抛出,若name为空,则在getGirlName方法抛出
     */
    @Test
    public void test3() {
        Boy boy = new Boy();
        //boy = null;
        String girlName = getGirlName(boy);
        System.out.println(girlName);
    }
}
           

普通优化

package cn.luis.lambda.atguigu.optional;

import org.junit.Test;

import java.util.Optional;

public class OptionalTest {

    /**
     * 普通优化
     */
    public String getGirlName1(Boy boy) {
        if (boy != null) {
            Girl girl = boy.getGirl();
            if (girl != null) {
                return boy.getGirl().getName();

            }
        }
        return null;
    }

    /**
     * 普通优化
     * 如果boy为null,则空指针会在本类抛出,若name为空,则在getGirlName方法抛出
     */
    @Test
    public void test33() {
        Boy boy = new Boy();
        //boy = null;
        String girlName = getGirlName1(boy);
        System.out.println(girlName);
    }
}
           

使用Optional优化

package cn.luis.lambda.atguigu.optional;

import org.junit.Test;

import java.util.Optional;

public class OptionalTest {

    /**
     * Optional优化
     */
    public String getGirlName2(Boy boy) {
        Girl girl = boy.getGirl();
        Optional<Girl> girlOptional = Optional.ofNullable(girl);
        Girl girl1 = girlOptional.orElse(new Girl("Mary"));
        System.out.println(girl1.getName());
        return null;
    }

    /**
     * Optional优化
     */
    @Test
    public void test333() {
        //Boy boy = new Boy();
        Boy boy = new Boy(new Girl("LiLy"));
        Optional<Boy> boyOptional = Optional.ofNullable(boy);
        Boy boy1 = boyOptional.orElse(new Boy());
        String girlName = getGirlName1(boy1);
        System.out.println(girlName);
    }
}

           

5. 可重复注解、类型注解

5.1 可重复注解

重复注解:1. 定义注解,2.定义注解容器,3在注解上添加@Repeatable(容器注解.class)
  • 自定义注解
@Repeatable(MyAnns.class)
@Target({TYPE, METHOD,FIELD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnn {

    String value() default "luis";
}
           
  • 定义一个注解容器
@Target({TYPE, METHOD,FIELD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnns {

    MyAnn[] value();
}
           
  • 测试类
public class AnnotationTest {

     /**
     * 重复注解:1. 定义注解,2.定义注解容器,3在注解上添加@Repeatable(容器注解.class)
     */
    @MyAnn("hello")
    @MyAnn("world")
    public void show() {
    }

    /**
     * 利用反射获取注解的值
     */
    @Test
    public void test1(){
        Class<AnnotationTest> clazz = AnnotationTest.class;
        try {
            Method showMethod = clazz.getMethod("show");

            MyAnn[] ann = showMethod.getAnnotationsByType(MyAnn.class);
            for (MyAnn myAnn : ann) {
                System.out.println(myAnn.value());
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}
           

5.2 类型注解

java8需要搭配框架或者自己定义注解才可
public class AnnotationTest {

    /**
     * 类型注解,java8需要搭配框架或者自己定义注解才可
     * @Target({ElementType.TYPE, METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE})
     */
    // 提供编译时检查,不允许为null
    public /*@NonNull*/ Object obj = null;
}
           

6. ForkJoin框架

[了解]:对并发优化!
package cn.luis.lambda.atguigu.fork_join;

import java.util.concurrent.RecursiveTask;

/**
 * @author Luis
 * @version 1.0
 * @description TODO  RecursiveAction:无返回值
 * @date 2020/12/9 9:51
 */
public class FoekJoinCalculate extends RecursiveTask<Long> {

    private static final long serialVersionUID = 134656970987L;

    private long start;
    private long end;
    // 临界值
    private static final long THRESHOLD = 10000;

    public FoekJoinCalculate(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        long length = end - start;

        if (length <= THRESHOLD) {
            long sum = 0;

            for (long i = start; i <= end; i++) {
                sum += 1;
            }
            return sum;
        } else {
            long middle = (start + end) / 2;
            FoekJoinCalculate left = new FoekJoinCalculate(start, middle);
            // 拆分子任务,同时压入线程队列
            left.fork();

            FoekJoinCalculate right = new FoekJoinCalculate(middle + 1, end);
            // 拆分子任务,同时压入线程队列
            right.fork();

            return left.join()+right.join();
        }
    }
}

           
  • 测试类
package cn.luis.lambda.atguigu.fork_join;

import org.junit.Test;

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

/**
 * @author Luis
 * @version 1.0
 * @description TODO
 * @date 2020/12/9 10:20
 */
public class ForkTest {

    /**
     * ForkJoin框架 (和普通for对比,要数据特大才有效果,因为拆分也需要时间)
     */
    @Test
    public void test1() {
        // 计算时间戳
        Instant start = Instant.now();

        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> task = new FoekJoinCalculate(0, 10000000000L); //1876
        Long sum = pool.invoke(task);

        System.out.println(sum);

        Instant end = Instant.now();

        System.out.println("耗费时间:" + Duration.between(start, end).toMillis()); //34
    }

    /**
     * 普通for
     */
    @Test
    public void test2() {
        // 计算时间戳
        Instant start = Instant.now();
        long sum = 0L;
        for (long i = 0; i <= 10000000000L; i++) {
            sum += i;
        }
        System.out.println(sum);
        Instant end = Instant.now();

        System.out.println("耗费时间:" + Duration.between(start, end).toMillis()); //4324
    }

    /**
     * java8并行流
     */
    @Test
    public void test3(){
        // 生成long型随机数

        // 串行流
        long reduce = LongStream.rangeClosed(0, 10000000000L)
                .reduce(0, Long::sum);
        System.out.println(reduce);

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

        // 串行流
        long reduce2 = LongStream.rangeClosed(0, 10000000000L)
                .parallel()
                .reduce(0, Long::sum);
        System.out.println(reduce2);


    }

}
           
本文总结自B站-尚硅谷

END