JDK1.8新特性详解

Lou.Chen2022年7月8日
大约 31 分钟

JDK8特性详解

一、接口的默认方法和静态方法

注:

  • 接口的中的成员默认且必须为 public static final 且标识符最好为全大写,必须赋默认值

  • 接口方法不能有实现。默认且必须为public abstarct

  • 接口中的静态方法必须要有实现

Java 8允许我们给接口

  • 允许添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法
    • 这里的default还是公共方法
  • 允许添加一个静态方法,必须有实现

注: 被default修饰的默认接口方法必须有实现

public interface Formula {
    double caculate(int a);

    default double sqrt(int a) {
        return Math.sqrt(a);
    }
    
    static void say(){
        System.out.println("hello")
    }
}

直接在caculate方法中使用接口中定义的default方法即可

public class Test {
    public static void main(String[] args) {
        Formula formula=new Formula(){
            @Override
            public double caculate(int a) {
                return sqrt(a * 100);
            }
        };
        double caculate = formula.caculate(1);
        System.out.println(caculate);

    }
}

在Java中只有单继承,如果要让一个类赋予新的特性,通常是使用接口来实现

二、Lambda表达式

使用限制:

如果要使用接口的中的Lambda中的表达,那么接口中需要满足如下条件:

  • 普通的接口方法只能有一个

  • 可以存在default方法(必须实现)

  • 可以存在static方法(必须实现)

  • 可以存在覆盖Object中的方法

语法:(parameter1,parameter2.....) -> {expression;};

**提示:**我们可以在接口上加@FunctionalInterface来判断我们的接口是否是函数式接口。

返回语句:

如果有一条语句,则无需写{}

如果有两条及其以上的语句,则必须加{}

①无参数,无返回值

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

②有一个参数,无返回值

(a)->System.out.println("hello");
//或者
a ->System.out.println("hello");

③有多个参数,无返回值

(a,b)-> System.out.println("hello");

④有多个参数,有一条语句的返回值

只有一条语句,则return 和 {}可以省略。

(a,b)-> a*b

④有多个参数,有多条语句的返回值

需要加{},且需要return关键字

(a,b)->{
    c=a*b;
    System.out.println("hello");
    return c;
}

接口

public interface NumberInterface{

    //普通的接口方法只能以一个,多个的话,使用lamabda会报错
    int caculate(int a,int b);

    default int opearate(int a,int b,NumberInterface numberInterface){
        return numberInterface.caculate(a, b);
    }

    //可以存在默认方法
    default String getMessage() {
        return "hello java";
    }

    //可以存在静态方法
    static void say() {
        System.out.println("static say");
    }

    //可以存在覆盖Object的方法
    @Override
    boolean equals(Object obj);

    //可以存在覆盖Object的方法
    @Override
    public String toString();
}

实现:

public class LamadaT {
    public static void main(String[] args) {
        int numberA=10;
        int numberB=5;
        NumberInterface add=(a,b)->a+b;
        NumberInterface sub=(a,b)->a-b;
        NumberInterface multi=(a,b)->a*b;
        NumberInterface div=(a,b)->a/b;
        System.out.println("加:"+add.caculate(numberA, numberB));
        System.out.println("减:"+sub.caculate(numberA, numberB));
        System.out.println("乘:"+multi.caculate(numberA, numberB));
        System.out.println("除:"+div.caculate(numberA, numberB));
    }
}

注意:

①lambda不可以定义一个和外层变量名相同的变量,与变量类型无关

②lambda中使用外层的变量,不能在lamdba表达式中修改其值,外层变量可以不是final类型的(jdk自动帮我们加了final),但是绝对不可以在lambda表达式中修改外层变量的值。

③lambda表达式主要为了执行方法类型的接口,避免定义匿名方法,开发更加简洁;

④lambda表达式中不能使用外部变量只能使用外部常量。即一般的情况不能使用for循环中的变量,需要在for循环中重新定义变量赋值。因为使用外面的变量lambda自动帮我们加final

三、四大核心内置接口

1、Consumer<T> 消费型接口

消费消息(有参无返)。提供一个T类型的参数,无返回值

原接口:

@FunctionalInterface
public interface Consumer<T> {
    
    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

测试:

public class ConsumerTest {
    public static void main(String[] args) {

        new ConsumerTest().getMoney(2000.0, m-> System.out.println("取钱:"+m));
    }

    public void getMoney(double money, Consumer<Double> consumer) {
//        消费
        consumer.accept(money);
    }
}

2、Supplier<T> 供给型接口

生产消息(无参有返),无需提供参数,返回一个T类型的参数

原接口:

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

测试:

public class SupplierTest {
    public static void main(String[] args) {
        //返回 20个随机整数
        List<Integer> numberList = new SupplierTest().getNumberList(20, () -> (int)(Math.random()*10));
        System.out.println(numberList.toString());
    }

    public <T> List<T> getNumberList(int num, Supplier<T> supplier) {
        List<T> list=new ArrayList<>();
        for (int i = 0; i < num; i++) {
            T t = supplier.get();
            list.add(t);
        }
        return list;
    }
}

3、Function<T,R> 函数型接口

接收一个T类型的参数,返回R类型的参数

原接口:

@FunctionalInterface
public interface Function<T, R> {
    
    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }


    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

测试:

public class FunctionTest {
    public static void main(String[] args) {
        String s = new FunctionTest().HandlerStr("i like java", (t) -> t.toUpperCase());
        System.out.println(s);
    }

    public <T, R> R HandlerStr(T t, Function<T, R> function) {
        return function.apply(t);
    }
}

4、Predicate<T> 断言型接口

接收一个T类型的参数,返回一个布尔值

原接口:

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

测试:

public class PredicateTest {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(2, 3, 4, 5, 6, 23, -233, -231, -33, 0);
        //过滤小于等于0的数
        List<Integer> resultList = new PredicateTest().filterObject(list, (t) -> t <= 0);
        System.out.println(resultList);
    }

    public <T> List<T> filterObject(List<T> list, Predicate<T> predicate) {
        List<T> allList=new ArrayList<>();
            list.forEach(c->{
//                返回判断的结果
                if(predicate.test(c)){
//                    过滤添加
                    allList.add(c);
                }
            });
        return allList;
    }
}

四、方法引用与构造器引用(双冒号)

实际上就是对Lambda表达式的再简写:: 算符在Java 8中被用作方法引用(method reference),方法引用是与lambda表达式相关的一个重要特性。它提供了一种不执行方法的方法。为此,方法引用需要由兼容的函数接口组成的目标类型上下文。

​ 大致意思是,使用lambda表达式会创建匿名方法, 但有时候需要使用一个lambda表达式只调用一个已经存在的方法(不做其它), 所以这才有了方法引用!

  • 当要传递给Lambda体的操作,已经有了实现方法,可以使用方法引用。

  • 方法引用可以看做是Lambda表达式的深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数是接口的一个实例。

  • 要求:*实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致。*

  • 格式:使用操作符“::”将类(或对象)与方法名分隔开。如下三种主要是用情况:

    • 对象名::实现方法(非静态方法)
    • 类::静态方法名
    • 类::实例方法(非静态方法)

一、方法引用

1、类对象::实例方法
  • Lambda的参数列表和返回值必需和方法引用中的方法参数列表和返回值一致
public static void main(String[] args) {

//        1、lambad的实现方式
//        具体的实现(lambda代替匿名内部类实现的接口)
        Consumer<String> consumer=(s)-> System.out.println(s);
//        方法调用
        consumer.accept("hello world!");
        
        //得到打印流对象
        PrintStream out = System.out;
        Consumer<Integer> consumer1 = (s) -> out.println(s);
        consumer1.accept(100);

        
//        2、::的实现方式。
//      Consumer<String> consumer2=out::println;
        Consumer<String> consumer2= System.out::println;
        consumer2.accept("i like java");
}

如下

Consumer<T>中的accept方法

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

System.out获取PrintStream对象中的println方法

public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
}

例2:

public class Employee {
    private String name;
    private Integer age;
    private Double salary;

    public Employee() {
    }

    public Employee(String name, Integer age, Double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    //getter
    //setter
    //toString
}
public static void main(String[] args) {
        Employee employee=new Employee();
        //这里我们提供一个消费型接口,要使用的接口中 有参无返回值
        //所有我们使用 :: 即使用Employee中的setSalary为有参无返回值。
        Consumer<Double> consumer=employee::setSalary;
        consumer.accept(21.2);
        //输出:Employee{name='null', age=null, salary=21.2}
        System.out.println(employee);

        Employee employee1=new Employee();
        employee1.setName("louchen");
//        这里我们使用供给型接口,所使用的都是 无参有返回值的方法
        Supplier<String> supplier=employee1::getName;
        String s = supplier.get();
        //输出:louchen
        System.out.println(s);
    }
2、类::静态方法
  • Lambda的参数列表和返回值必需和方法引用中的静态方法参数列表和返回值一致
public class Demo {
	@Test
	public void test() {
		List<String> list = Arrays.asList("aaaa", "bbbb", "cccc");
		
		//静态方法语法	ClassName::methodName
		list.forEach(Demo::print);
	}
	
	public static void print(String content){
		System.out.println(content);
	}
}
public static void main(String[] args) {
//        1、lambda形式
        Comparator<Integer> comparator = (a, b) -> a.compareTo(b);
        int compare = comparator.compare(2, 6);
        //输出: -1
        System.out.println(compare);

//        2、::形式
//        Comparator 使用的参数列表 int compare(T o1, T o2);
//        Integer.compare方法的参数列表 public static int compare(int x, int y)
//        都是相同参数列表,相同返回值 即可使用 ::
        Comparator<Integer> comparator1=Integer::compare;
        int compare1 = comparator1.compare(2, 1);
        System.out.println(compare1);
}
3、类::实例方法

这里String类中的实例方法public String toUpperCase(){},因为 map中的函数式接口R apply(T t); 是一个无参有返回值的接口方法,所以这里的参数t实际上时调用者this对象,也就是list集合中的对象,而String中的toUpperCase()也就是函数式接口R apply(T t);的实现作为返回值

List<String> list=new ArrayList<>();
        list.add("zs");
        list.add("ls");
        list.add("ww");
        list.add("zl");
list.stream().map(String::toUpperCase).forEach(System.out::println);
  • 这里的 String::toUpperCase使用String中的toUpperCase 实例方法,实际上是把调用toUpperCase() 方法的对象作为Lambda表达式中的第一个参数,执行方法的返回结果相当于Lambda表达式中的返回值

  • System.out::println 使用的是System.out得到的PrintStream的实例对象调用的println(String x)方法必须和Lambda中的参数类别和返回值一致

4、常见情形

Comparator接口中使用的函数式接口int compare(T o1, T o2); Lambad表达式为 两参一返回 的形式

Comparator<Integer> comparator1=Integer::compare;

  • static int compare(int x, int y) 为静态方法,符合 类名::静态方法 的形式,所以满足方法引用和Lambda的参数列表和返回值一致

Comparator<Integer> comparator1=Integer::compareTo;

  • int compareTo(Integer anotherInteger) 为实例方法, 符合 类名::实例方法 的形式,即Lambda的第一个参数为调用当前实例方法的对象,而第二个参数才为该方法的参数,返回值为执行该方法的返回值,所以符合Lambda表达式的规则

所以这两种方式等价

二、构造器引用

注意构造器的参数列表要与函数式接口中的抽象方法的参数类别保持一致,使用哪个构造器和接口中的抽象方法有关

1、类名:new
public class Employee {
    private String name;
    private Integer age;
    private Double salary;

    public Employee() {
    }

    public Employee(Integer age) {
        this.age = age;
    }

    public Employee(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    //getter
    //setter
    //tostring 
}
public static void main(String[] args) {
//        生产型接口: lambda形式生成Employee对象
        Supplier<Employee> supplier=()->new Employee();
        Employee employee = supplier.get();

//        生产型接口:
//        这里我们new的为无参构造器。因为接口中的为无参列表
        Supplier<Employee> supplier1=Employee::new;
        Employee employee1 = supplier1.get();

        //方法型接口,传入Integer类型,返回Employe对象
//        这里我们使用的为一个参数(Integer)的构造器,因为接口中的为一个参数的列表
        Function<Integer,Employee> function=Employee::new;
        Employee apply = function.apply(20);
//       输出: Employee{name='null', age=20, salary=null}
        System.out.println(apply);

        //这里我们使用String,Integer类型作为参数列表,返回Employee对象
//        即使用两个参数列表的构造器
        BiFunction<String,Integer,Employee> function1=Employee::new;
        Employee employee2 = function1.apply("张三", 20);
//        输出:Employee{name='张三', age=20, salary=null}
        System.out.println(employee2);
}

BiFunction函数接口:

@FunctionalInterface
public interface BiFunction<T, U, R> {

    R apply(T t, U u);
}

三、数组引用

1、Type[]:new
public static void main(String[] args) {
//        1、函数型接口:lambda形式创建一个值得大小的字符串数组
        Function<Integer, String[]> function = (n) -> new String[n];
        String[] apply = function.apply(10);
//        10
        System.out.println(apply.length);

//        2、::形式
        Function<Integer,String[]> function1=String[]::new;
        String[] apply1 = function1.apply(20);
//        20
        System.out.println(apply1.length);
    }

五、Stream

1、什么是Stream

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

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

注意:

  • ①Stream 自己不会存储元素
  • ②Stream 不会改变源对象。相反,他们返回一个持有结果的新Stream
  • ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行

2、Steam操作的步骤

①创建Stream

一个数据源(如:集合,数组),获取一个流

②中间操作

一个中间操作链,对数据源的数据进行处理

③终止操作(终端操作)

一个终止操作,执行中间操作链,并产生结果。

3、创建Stream

public static void main(String[] args) {
//        1、通过Collection系列集合提供的stream() 或 parallelStream() 并行流
        List<String> list=new ArrayList<>();
//        得到Stream对象
        Stream<String> stream = list.stream();

//        2、通过Arrays 中的静态方法stream() 获取数组流
        Employee[] employees = new Employee[10];
        Stream<Employee> stream1 = Arrays.stream(employees);

//        3、通过Stream类中的静态方法of()
        Stream<String> stream2 = Stream.of("aa", "bb", "cc", "dd");


//        4、创建无限流 无限迭代
//                                             开始的元素; 对元素的操作
        Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2);
//        无线循环打印
//        stream3.forEach(System.out::println);
//        打印10条
        stream3.limit(10).forEach(System.out::println);

//        4、生成无限流
        Stream<Double> generate = Stream.generate(() -> Math.random() * 10);
//        无线循环打印
//        generate.forEach(System.out::println);
//        打印5条
        generate.limit(5).forEach(System.out::println);
    }
0
2
4
6
8
10
12
14
16
18

8.063997726244807
2.473207418080723
9.47397043397395
9.998528480050133
1.0465136543742837

4、中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理。而在终止操作时一次性全部处理。称为“惰性求值”。 只要没有执行终止操作,中间操作就不会执行。

中间操作的返回值还是中间操作

① 筛选与切片
public class Employee {
    private String name;
    private Integer age;
    private Double salary;
    public Employee(String name, Integer age, Double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    //getter
    //setter
    //tostring
}
public class T2 {
	static List<Employee> list= Arrays.asList(
                new Employee("张三",10,2000.0),
                new Employee("李四",20,3000.0),
                new Employee("王五",30,4000.0),
                new Employee("赵六",40,5000.0),
                new Employee("田七",50,6000.0),
                new Employee("钱八",60,7000.0)
    );
}
filter

接收lambda,从流中排除一些元素 里面对满足条件的boolean为true的元素才返回

Stream API帮我们操作的过程 也叫内部迭代

 public static void main(String[] args) {
        Stream<Employee> stream = list.stream();
//        中间操作  只要没有执行终止操作,中间操作就不会执行
        stream.filter((e) -> e.getAge() > 30)
//                终止操作
                .forEach(System.out::println);
    }
Employee{name='赵六', age=40, salary=5000.0}
Employee{name='田七', age=50, salary=6000.0}
Employee{name='钱八', age=60, salary=7000.0}

外部迭代:

public void outIterator(){
        Iterator<Employee> iterator = list.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
}
limit

截断流,是元素不超过给定数量

Stream<Employee> stream = list.stream();
//        中间操作
        stream.filter((e) -> e.getAge() > 30)
            //中间操作
                .limit(2)
//                终止操作
                .forEach(System.out::println);
Employee{name='赵六', age=40, salary=5000.0}
Employee{name='田七', age=50, salary=6000.0}
skip(n)

跳过当前满足条件的n个元素,输出跳过满足n个条件后的所有元素

若流中的元素不足n个,则返回一个空流。与limit(n)互补

public static void main(String[] args) {
        Stream<Employee> stream = list.stream();
//        中间操作
        stream.filter((e) -> e.getSalary() >= 2000)
//                中间操作 跳过操作
                .skip(2)
//                终止操作
                .forEach(System.out::println);
}
distinct

通过流所生成元素的hashCode()和equals()去除重复元素

这里的distinct去重的是上一个加工后的steam中的内容

public class Employee {
    private String name;
    private Integer age;
    private Double salary;

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Employee employee = (Employee) o;
        return Objects.equals(name, employee.name) &&
                Objects.equals(age, employee.age) &&
                Objects.equals(salary, employee.salary);
    }
    //getter
    //setter
    //toString
}
public class T2 {
    static List<Employee> list= Arrays.asList(
            new Employee("张三",10,2000.0),
            new Employee("李四",20,3000.0),
            new Employee("王五",30,4000.0),
            new Employee("赵六",40,5000.0),
            new Employee("田七",50,6000.0),
            new Employee("钱八",60,7000.0),
            new Employee("钱八",60,7000.0),
            new Employee("钱八",60,7000.0)
    );
}
public static void main(String[] args) {
        Stream<Employee> stream = list.stream();
//        中间操作
        stream.filter((e) -> e.getSalary() >= 2000)
//                中间操作 跳过
                .skip(2)
//                中间操作 去重 (必须重写hashcode 和 equals)
                .distinct()
//                终止操作
                .forEach(System.out::println);
    }
Employee{name='王五', age=30, salary=4000.0}
Employee{name='赵六', age=40, salary=5000.0}
Employee{name='田七', age=50, salary=6000.0}
Employee{name='钱八', age=60, salary=7000.0}
②映射
public class Employee {
    private String name;
    private Integer age;
    private Double salary;
    public Employee(String name, Integer age, Double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    //getter
    //setter
    //tostring
}
public class T3 {
    static List<Employee> employeeList= Arrays.asList(
            new Employee("张三",10,2000.0),
            new Employee("李四",20,3000.0),
            new Employee("王五",30,4000.0),
            new Employee("赵六",40,5000.0),
            new Employee("田七",50,6000.0),
            new Employee("钱八",70,7000.0),
            new Employee("钱八",80,7000.0),
            new Employee("钱八",60,7000.0)
        
 	}
    List<String> list = Arrays.asList("a", "bb", "ccc", "dddd");
    // 获得传入字符中的所有字符
    public static Stream<Character> getAllCharacter(String str) {
        List<Character> list=new ArrayList<>();
        for(Character c:str.toCharArray()){
            list.add(c);
        }
        return list.stream();
    }
 }
map

接收lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素 映射出的返回值即对应新的元素

//1、将字符串全部转为为小写
        List<String> list = Arrays.asList("a", "bb", "ccc", "dddd");
        list.stream()
                .map(s->s.toUpperCase())
                .forEach(System.out::println);

//2、获取所有员工的姓名
        Stream<Employee> stream = T3.employeeList.stream();
        stream.map(e -> e.getName())
//                去重(只是对name去重)
                .distinct()
                .forEach(System.out::println);

A
BB
CCC
DDDD

张三
李四
王五
赵六
田七
钱八
        //3、因为中间操作Stream<T>中的T是map操作中getAllCharacter的返回值
        Stream<Stream<Character>> streamStream = list.stream()
                .map(T3::getAllCharacter);
        //遍历得到Stream<Character>
        streamStream.forEach(s->{
//            再次遍历得到character
            s.forEach(System.out::println);
        });
a
b
b
c
c
c
d
d
d
d

我们看Stream中的map源码发现:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

我们传入一个字符串参数T,返回的是一个Stream<Character>流,所以这里的R即为Stream<Character>

所以map方法在上述例子的返回值为 Stream<Stream<Character>> 即把每一个字符串拆分后的小流放入一个大流中存储。所以需要遍历两次。

flatmap(平行流),二次子处理

接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

把许多小流中的元素直接拿出来放入大流中

        Stream<Character> characterStream = list.stream().flatMap(T3::getAllCharacter);
        characterStream.forEach(System.out::println);

我们看Stream中的flatMap源码可发现:

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

我们传入一个字符串参数T,返回一个Stream<Character>流,注意这里的R为返回流中的泛型参数类型,所以这里的R即为Character,所以flatmap的返回类型为Stream<Character>

③排序sorted
// 代表某集合
List<> list = new ArrayList(); 

// 按照属性一升序
list.stream().sorted(Comparator.comparing(::属性一));

// 按照属性一降序
// 方法一 先升序后降序
list.stream().sorted(Comparator.comparing(::属性一).reversed());
// 方法二 直接降序
list.stream().sorted(Comparator.comparing(::属性一,Comparator.reverseOrder()));

// 多条件排序
// 属性一升序,属性二升序
list.stream().sorted(Comparator.comparing(::属性一).thenComparing(::属性二));
自然排序(comparable)

要排序的对象需要实现comparable接口

public class Employee implements Comparable<Employee>{
    private String name;
    private Integer age;
    private Double salary;
    //getter setter tostring
    @Override
    public int compareTo(Employee o) {
        //按照姓名 升序排列
        return this.name.compareTo(o.name);
    }
}
static List<Employee> employeeList= Arrays.asList(
            new Employee("a",20,3000.0),
            new Employee("c",30,4000.0),
            new Employee("g",10,2000.0),
            new Employee("f",60,7000.0),
            new Employee("g",40,5000.0),
            new Employee("q",70,7000.0),
            new Employee("t",50,6000.0),
            new Employee("g",80,7000.0)
    );
    public static void main(String[] args) {
        employeeList.stream()
                .sorted()
                .forEach(System.out::println);
    }
Employee{name='a', age=20, salary=3000.0}
Employee{name='c', age=30, salary=4000.0}
Employee{name='f', age=60, salary=7000.0}
Employee{name='g', age=10, salary=2000.0}
Employee{name='g', age=40, salary=5000.0}
Employee{name='g', age=80, salary=7000.0}
Employee{name='q', age=70, salary=7000.0}
Employee{name='t', age=50, salary=6000.0}
定制排序(comparator)

注意:这里的定制排序的规则高于自然排序的规则

public static void main(String[] args) {
        employeeList.stream()
                .sorted((a,b)->{
//                    如果姓名相同
                    if (a.getName() == b.getName()) {
//                        如果姓名相同 按照年龄降序排序
                        return b.getAge().compareTo(a.getAge());
                    }else{
//                        姓名不相同,按照年龄升序排列
                        return a.getName().compareTo(b.getName());
                    }
                })
                .forEach(System.out::println);
    }
Employee{name='a', age=20, salary=3000.0}
Employee{name='c', age=30, salary=4000.0}
Employee{name='f', age=60, salary=7000.0}
Employee{name='g', age=80, salary=7000.0}
Employee{name='g', age=40, salary=5000.0}
Employee{name='g', age=10, salary=2000.0}
Employee{name='q', age=70, salary=7000.0}
Employee{name='t', age=50, salary=6000.0}

5、终止操作

​ 数据经过中间加工操作,就轮到终止操作符上场了;终止操作符就是用来对数据进行收集或者消费的,数据到了终止操作这里就不会向下流动了,终止操作符只能使用一次。

①查找与匹配
public class Employee{

    private String name;
    private Integer age;
    private Double salary;
    private Status status;
    //getter setter tostring
    public enum Status{
        FREE,
        BUSY,
        VOCATION;
    }
}
public class T5 {
    static List<Employee> employeeList= Arrays.asList(
            new Employee("a",20,3000.0, Status.BUSY),
            new Employee("c",30,4000.0,Status.FREE),
            new Employee("g",10,2000.0,Status.BUSY),
            new Employee("f",60,7000.0,Status.VOCATION),
            new Employee("g",40,5000.0,Status.VOCATION),
            new Employee("q",70,7000.0,Status.BUSY),
            new Employee("t",50,6000.0,Status.VOCATION),
            new Employee("g",80,7000.0,Status.FREE)
    );
}
allMatch

检查给定的条件是否匹配所有元素 返回一个boolean

public static void main(String[] args) {
        boolean b = employeeList.stream()
            //传入一个断言型接口
            //判断使用所有的age是否都大于等于10
                .allMatch(e -> e.getAge() >= 10);
    	//true
        System.out.println(b);
}
anyMatch

检查是否至少匹配一个元素 返回一个boolean

               //传入一个断言型接口
        boolean b1 = employeeList.stream()
//                是否匹配至少一个age为80的元素
                .anyMatch(e -> e.getAge().equals(80));
		//true
        System.out.println(b1);
noneMatch

检查是否没有匹配 所指定的条件的元素 返回boolean

        boolean a = employeeList.stream()
//                传入断言型接口
             //是否没有匹配姓名为t的
                .noneMatch(e -> e.getName().equals("t"));
		//false
        System.out.println(a);
findFirst

返回满足条件的第一个元素 一般我们排序之后取的第一个元素

        Optional<Employee> first = employeeList.stream()
//               传入一个比较器接口
//               按照薪资从大到小排序
                .sorted((c,d)->-c.getSalary().compareTo(d.getSalary()))
//                返回第一个元素
                .findFirst();
//        从Optional接口中取出元素
//Employee{name='f', age=60, salary=7000.0, status=VOCATION}
        System.out.println(first.get());
findAny

返回匹配流中的任意一个元素

注意这里我们获取流:employeeList.stream() 为串型流,会阻塞线程

employeeList.parallelStream() 为平行流,不会阻塞线程。同时多个线程匹配返回

        Optional<Employee> any = employeeList.stream()
            //过滤status为VOCATION的元素
                .filter(e -> e.getStatus().equals(Status.VOCATION))
            //返回任意一个
                .findAny();
        System.out.println(any.get());
sum

int、double、long:

double max = list.stream().mapToDouble(User::getHeight).sum();

BigDecimal:

BigDecimal bb=list.stream().map(Plan::getAmount).reduce(BigDecimal.ZERO,BigDecimal::add);
count

返回匹配的所有元素的个数

        long count = employeeList.stream()
            //过滤薪资大于3000的元素
                .filter(r->r.getSalary()>3000)
                .count();
//        8
        System.out.println(count);
max

返回流中的最大值

        Optional<Employee> max = employeeList.stream()
//                必须有在max中实现一个比较的接口器 
//                按照年龄排序后,取出年龄最大的
                .max((e, f) -> e.getAge().compareTo(f.getAge()));
		//Employee{name='a', age=80, salary=7000.0, status=VOCATION}
        System.out.println(max.get());
min

返回流中的最小值

        Optional<Double> min = employeeList.stream()
//                得到所有薪资值
//                这里我们使用Function中的 R apply(T t); 方法。 T为传入的参数:Employee  R为返回的值
//                我们已经传入了T,现在只需要传入R即可 ;可以直接使用Employee中的getSalary()返回Double即可
                .map(Employee::getSalary)
//                //必须有在max中实现一个比较的接口器
//               求出薪资最少的
                .min(Double::compare);
			//2000.0
        System.out.println(min.get());
注意这里的min和max里面的排序规则必须为升序,否则得出的结果会相反
②规约
public class Employee{

    private String name;
    private Integer age;
    private Double salary;
    private Status status;
    public enum Status{
        FREE,
        BUSY,
        VOCATION;
    }
    //getter setter tostring
}
public class T6 {
    static List<Employee> employeeList= Arrays.asList(
            new Employee("a",20,3000.0, Status.BUSY),
            new Employee("c",30,4000.0,Status.FREE),
            new Employee("g",10,2000.0,Status.BUSY),
            new Employee("f",60,7000.0,Status.VOCATION),
            new Employee("g",40,5000.0,Status.VOCATION),
            new Employee("q",70,7000.0,Status.BUSY),
            new Employee("t",50,6000.0,Status.VOCATION),
            new Employee("g",80,7000.0,Status.FREE)
    );
reduce

**方法一:**可以将流中元素反复结合起来,得到一个值。返回T

**注意:**注意该值不可能为空,因为我们使用的时候会传入一个初始值identity

T reduce(T identity, BinaryOperator<T> accumulator);

**方法二:**可以将流中元素反复结合起来,得到一个值。返回Optional<T>

Optional<T> reduce(BinaryOperator<T> accumulator);

**注意:**这里没有初始值,所以我们的类型会可能出现null的情况,所以我们需要用Optional<T>来处理

BinaryOperator接口:

public interface BinaryOperator<T> extends BiFunction<T,T,T> {
}

BiFunction接口:

public interface BiFunction<T, U, R> {
    R apply(T t, U u);
}
public static void main(String[] args) {
    	//相加等于51
        List<Integer> list = Arrays.asList(1, 2, 3, 5, 6, 7, 8, 9, 10);

        Integer reduce = list.stream()
            //默认初始值1;  传入BinaryOperator<T>接口。接口中的方法 R apply(T t, U u);
                .reduce(1, (a, b) -> a + b);
    	//52  总的值加上初始值1
        System.out.println(reduce);
    }

累加原理 :

  • 初始值加上第一个元素(1+1=2)
  • 得到的结果加上第二元素(2+2=4)
  • 得到的结果加上第三个元素(4+3)
  • .......
Optional<Double> reduce1 = employeeList.stream()
    		//过滤出所有的薪资
                .map(Employee::getSalary)
    		//Double中的sum静态方法
                .reduce(Double::sum);
		//41000.0
		System.out.println(reduce1.get());
③收集
public class T7 {
    static List<Employee> employeeList= Arrays.asList(
            new Employee("a",20,3000.0, Status.BUSY),
            new Employee("c",30,4000.0,Status.FREE),
            new Employee("g",10,2000.0,Status.BUSY),
            new Employee("f",60,7000.0,Status.VOCATION),
            new Employee("g",40,5000.0,Status.VOCATION),
            new Employee("q",70,7000.0,Status.BUSY),
            new Employee("t",50,6000.0,Status.VOCATION),
            new Employee("g",80,7000.0,Status.FREE)
    );
public class Employee{

    private String name;
    private Integer age;
    private Double salary;
    private Status status;
    //getter setter tostring
    public enum Status{
        FREE,
        BUSY,
        VOCATION;
    }
}
collect

将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法

<R, A> R collect(Collector<? super T, A, R> collector);

Collector 接口中的方法实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是Collectors工具类提供了很多对Collector 中接口实现的静态方法,可以方便地创建常见收集器实例

基本操作:
public static void main(String[] args) {
//        一、将集合中的所有员工姓名加入到一个list集合中
        List<String> collect = employeeList.stream()
//                映射出姓名
                .map(Employee::getName)
//                收集该元素,加到list结合中
                .collect(Collectors.toList());
//        [a, c, g, f, g, q, t, g]
        System.out.println(collect);

//        二、将集合中的所有员工姓名加入到一个set集合中
        Set<String> collect1 = employeeList.stream()
                .map(Employee::getName)
                .collect(Collectors.toSet());
//        [a, q, c, t, f, g]
        System.out.println(collect1);

//        三、将集合中的所有员工姓名加入到一个自定义集合中
        HashSet<String> collect2 = employeeList.stream()
                .map(Employee::getName)
                .collect(Collectors.toCollection(HashSet::new));
//        [a, q, c, t, f, g]
        System.out.println(collect2);

//        四、收集集合中元素的个数
        Long collect3 = employeeList.stream()
                .collect(Collectors.counting());
//        8
        System.out.println(collect3);

//        五、求集合中工资的平均值
        Double collect4 = employeeList.stream()
                .collect(Collectors.averagingDouble(Employee::getSalary));
//        5125.0
        System.out.println(collect4);

//        六、求工资总和
        Double collect5 = employeeList.stream()
                .collect(Collectors.summingDouble(Employee::getSalary));
//        41000.0
        System.out.println(collect5);

//        七、求工资最大值的对象  注意这里要排序
        Optional<Employee> collect6 = employeeList.stream()
                .collect(Collectors.maxBy((a, b) -> a.getSalary().compareTo(b.getSalary())));
//        Employee{name='f', age=60, salary=7000.0, status=VOCATION}
        System.out.println(collect6.get());

//        八、求工资的最小值
        Optional<Double> collect7 = employeeList.stream()
                .map(Employee::getSalary)
                .collect(Collectors.minBy((a, b) -> a.compareTo(b)));
//        2000.0
        System.out.println(collect7.get());

    }
分组:
//        1、按照员工的状态进行分组
        Map<Status, List<Employee>> collect = employeeList.stream()
                .collect(Collectors.groupingBy(Employee::getStatus));
        String s = new Gson().toJson(collect);
        System.out.println(s);
{
     "FREE": [
          {
               "name": "c",
               "age": 30,
               "salary": 4000,
               "status": "FREE"
          },
          {
               "name": "g",
               "age": 80,
               "salary": 7000,
               "status": "FREE"
          }
     ],
     "VOCATION": [
          {
               "name": "f",
               "age": 60,
               "salary": 7000,
               "status": "VOCATION"
          },
          {
               "name": "g",
               "age": 40,
               "salary": 5000,
               "status": "VOCATION"
          },
          {
               "name": "t",
               "age": 50,
               "salary": 6000,
               "status": "VOCATION"
          }
     ],
     "BUSY": [
          {
               "name": "a",
               "age": 20,
               "salary": 3000,
               "status": "BUSY"
          },
          {
               "name": "g",
               "age": 10,
               "salary": 2000,
               "status": "BUSY"
          },
          {
               "name": "q",
               "age": 70,
               "salary": 7000,
               "status": "BUSY"
          }
     ]
}
多级分组:
//        2、先按照员工状态分组,再按照工资分组
        Map<Status, Map<String, List<Employee>>> collect1 = employeeList.stream()
                .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy(a -> {
                    if (a.getSalary() <= 3000) {
                        return "码农";
                    } else if (a.getSalary() <= 5000) {
                        return "高级码农";
                    } else {
                        return "工程师";
                    }
                })));
        String s1 = new Gson().toJson(collect1);
        System.out.println(s1);
{
     "FREE": {
          "高级码农": [
               {
                    "name": "c",
                    "age": 30,
                    "salary": 4000,
                    "status": "FREE"
               }
          ],
          "工程师": [
               {
                    "name": "g",
                    "age": 80,
                    "salary": 7000,
                    "status": "FREE"
               }
          ]
     },
     "VOCATION": {
          "高级码农": [
               {
                    "name": "g",
                    "age": 40,
                    "salary": 5000,
                    "status": "VOCATION"
               }
          ],
          "工程师": [
               {
                    "name": "f",
                    "age": 60,
                    "salary": 7000,
                    "status": "VOCATION"
               },
               {
                    "name": "t",
                    "age": 50,
                    "salary": 6000,
                    "status": "VOCATION"
               }
          ]
     },
     "BUSY": {
          "码农": [
               {
                    "name": "a",
                    "age": 20,
                    "salary": 3000,
                    "status": "BUSY"
               },
               {
                    "name": "g",
                    "age": 10,
                    "salary": 2000,
                    "status": "BUSY"
               }
          ],
          "工程师": [
               {
                    "name": "q",
                    "age": 70,
                    "salary": 7000,
                    "status": "BUSY"
               }
          ]
     }
}
分区(true/false):
//        3、按照年龄区 true/false
        Map<Boolean, List<Employee>> collect2 = employeeList.stream()
                .collect(Collectors.partitioningBy(e -> e.getAge() >= 40));
        System.out.println(new Gson().toJson(collect2));
{
     "false": [
          {
               "name": "a",
               "age": 20,
               "salary": 3000,
               "status": "BUSY"
          },
          {
               "name": "c",
               "age": 30,
               "salary": 4000,
               "status": "FREE"
          },
          {
               "name": "g",
               "age": 10,
               "salary": 2000,
               "status": "BUSY"
          }
     ],
     "true": [
          {
               "name": "f",
               "age": 60,
               "salary": 7000,
               "status": "VOCATION"
          },
          {
               "name": "g",
               "age": 40,
               "salary": 5000,
               "status": "VOCATION"
          },
          {
               "name": "q",
               "age": 70,
               "salary": 7000,
               "status": "BUSY"
          },
          {
               "name": "t",
               "age": 50,
               "salary": 6000,
               "status": "VOCATION"
          },
          {
               "name": "g",
               "age": 80,
               "salary": 7000,
               "status": "FREE"
          }
     ]
}
汇总操作:
//        九、对员工年龄汇总操作
        IntSummaryStatistics collect8 = employeeList.stream()
                .collect(Collectors.summarizingInt(Employee::getAge));
        //80
        //45.0
        //10
        //8
        System.out.println(collect8.getMax());
        System.out.println(collect8.getAverage());
        System.out.println(collect8.getMin());
        System.out.println(collect8.getCount());
字符串连接操作:
        String collect9 = employeeList.stream()
                .map(Employee::getName)
                .collect(Collectors.joining());
//        acgfgqtg
        System.out.println(collect9);

//        加入指定连接符
        String collect10 = employeeList.stream()
                .map(Employee::getName)
//                每个元素的连接符; 前缀; 后缀
                .collect(Collectors.joining(",","[","]"));
//        [a,c,g,f,g,q,t,g]
        System.out.println(collect10);

6、Stream练习

public class Employee{

    private String name;
    private Integer age;
    private Double salary;
    private Status status;
    //getter setter tostring
    public enum Status{
        FREE,
        BUSY,
        VOCATION;
    }
}
    static List<Employee> employeeList= Arrays.asList(
            new Employee("a",20,3000.0,Status.BUSY),
            new Employee("c",30,4000.0,Status.FREE),
            new Employee("g",10,2000.0,Status.BUSY),
            new Employee("f",60,7000.0,Status.VOCATION),
            new Employee("g",40,5000.0,Status.VOCATION),
            new Employee("q",70,7000.0,Status.BUSY),
            new Employee("t",50,6000.0,Status.VOCATION),
            new Employee("g",80,7000.0,Status.FREE)
    );

1、给定一个数字列表,如何返回一由每个数的平方构成的列表呢?

[1,2,3,4,5] 返回 [1,4,9,16,25]

        Integer[] integers=new Integer[]{1,2,3,4,5};
        Stream<Integer> stream = Arrays.stream(integers);
        stream.map(i -> i * i)
                .forEach(System.out::println);

2、使用reduce和map统计流中有多少个Employee对象

Optional<Integer> reduce = employeeList.stream()
                .map((e) -> 1)
                .reduce((a, b) -> a + b);
		//8
        System.out.println(reduce.get());

其他练习:

Trader 交易员

public class Trader {
    private String name;
    private String city;
    //getter setter
}

Transaction 交易

public class Transaction {
    private Trader trader;
    int year;
    int value;
    //getter setter
}
public class T9 {
    static Trader zs=new Trader("Zss", "北京");
    static Trader ls=new Trader("Lsl", "上海");
    static Trader ww=new Trader("Wwv", "武汉");
    static Trader zl=new Trader("Zlz", "武汉");
    static List<Transaction> transactions= Arrays.asList(
            new Transaction(zs,2018,1300),
            new Transaction(zs,2019,3200),
            new Transaction(ls,2019,3200),
            new Transaction(ww,2018,3100),
            new Transaction(ww,2018,300),
            new Transaction(ls,2019,1000),
            new Transaction(zl,2018,800)
    );
    public static void main(String[] args) {
//        1、找出2018的交易额 并从大到小排序
        transactions.stream()
                .filter(e -> e.getYear() == 2018)
                .sorted((a, b) -> Integer.compare(b.getValue(), a.getValue()))
                .forEach(System.out::println);

//        2、交易员都在哪些城市工作过
        transactions.stream()
                .map(t->t.getTrader().getCity())
                .distinct()
                .forEach(System.out::println);

//        3、查找来自武汉的交易员,并按照姓名排序
        transactions.stream()
                .filter(c->c.getTrader().getCity().equals("武汉"))
                .map(Transaction::getTrader)
                .distinct()
                .sorted((d,e)->d.getName().compareTo(e.getName()))
                .forEach(System.out::println);

//        4、返回所有交易员的姓名字符串,按字母顺序排列
//        ①按照每个名称作为字符串拼接
        String reduce = transactions.stream()
                .map(f -> f.getTrader().getName())
                .distinct()
                .sorted()
//                拼接字符串
                .reduce("", String::concat);
//        LslWwvZlzZss
        System.out.println(reduce);

//        ②按照字符 排序忽略大小写
        transactions.stream()
                .map(g->g.getTrader().getName())
                .distinct()
                .flatMap(T9::getStrChar)
                .sorted(String::compareToIgnoreCase)
                .forEach(System.out::print);

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

//        5、有没有交易员在上海工作
        boolean re = transactions.stream()
                .anyMatch(h -> h.getTrader().getCity().equals("上海"));
        System.out.println(re);

//        6、查询生活在武汉的交易员的所有交易额
        Integer re1 = transactions.stream()
                .filter(i -> i.getTrader().getCity().equals("武汉"))
                .map(j -> j.getValue())
                .reduce(0, Integer::sum);
        System.out.println(re1);

//        7、在所有的交易额中,最高的是?
        Optional<Integer> max = transactions.stream()
                .map(k -> k.getValue())
                .max(Integer::compareTo);
        System.out.println(max.get());

//        8、最小的交易? 即整个对象
        Optional<Transaction> min = transactions.stream()
                .min((l, m) -> Integer.compare(l.getValue(), m.getValue()));
        System.out.println(min.get());

    }

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

7、并行流和顺序(串行)流

当我们计算一个1到100亿的累加操作

默认采用顺序流:

    public static void main(String[] args) {
        Instant start=Instant.now();
        LongStream.range(0, 10000000000L)
                .reduce(0,Long::sum);
        Instant end=Instant.now();
        //相加所用时间:4029 毫秒
        System.out.println("相加所用时间:"+ Duration.between(start, end).toMillis());
    }

采用并行流:

public static void main(String[] args) {
        Instant start=Instant.now();
        LongStream.range(0, 10000000000L)
                .parallel()
                .reduce(0,Long::sum);
        Instant end=Instant.now();
    	//相加所用时间:1212 毫秒
        System.out.println("相加所用时间:"+ Duration.between(start, end).toMillis())
    }

所谓并行流:并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。

https://www.jianshu.com/p/19ffb2c9438aopen in new window

Java8中将并行进行了优化,我们可以很容易地对数据进行行操作。Stream API可以声明性地通过parallel()sequential()在并行流与顺序流之间进行切换。

其实我们的并行流,底层还是采用的是Fork/Join框架:

​ Fork/Join框架采用“工作窃取”模式:当执行新的任务时,它可以将其拆分成更小的任务执行,并将小任务加到线程队列中。如果线程获取不到任务,它不会闲着,会从一个随机线程的队列中偷一个并把它放在自己的队列中,保证尽可能地利用CPU的资源。

8、Optional类

Optional主要为我们解决空指针的异常

Optional.of(T t)

创建一个Optional实例

    public static void main(String[] args) {
        // NullPointerException 传null直接在该行抛空指针
        Optional<Employee> optionalEmployee = Optional.of(null);

        Optional<Employee> optionalEmployee1 = Optional.of(new Employee());
//        Employee{name='null', age=null, salary=null, status=null}
        System.out.println(optionalEmployee1.get());
    }
Optional.empty()

创建一个空的Optional实例

    public static void main(String[] args) {
        //不会抛出空指针
        Optional<Employee> empty = Optional.empty();
        //获取元素 NoSuchElementException
        System.out.println(empty.get());
    }
Optional.ofNullable(T t)

若t不为null,创建Optional实例,否则创建空实例

相当于 Optional.of(T t)Optional.ofNullable(T t) 的结合

    public static void main(String[] args) {
        Optional<Employee> empty = Optional.ofNullable(new Employee());
        System.out.println(empty.get());
		
        //若传入空对象 则创建空Optional实例
        Optional<Employee> empty1 = Optional.ofNullable(null);
        //但是获取元素 NoSuchElementException
        System.out.println(empty.get());
    }
isPresent()

判断是否包含空值

public static void main(String[] args) {
        Optional<Employee> empty = Optional.ofNullable(new Employee("louchen",10,1000.0,Status.VOCATION));
//        判断Optional容器中是否有元素
        if(empty.isPresent()) {
            System.out.println(empty.get());
        }
    }
orElse(T t)

如果调用对象包含值,返回该值,否则返回t

    public static void main(String[] args) {
        Employee employee=null;
//        传入的为空
        Optional<Employee> empty = Optional.ofNullable(employee);
//        若为空 则使用创建的对象 否则使用ofNullable传入的。
        Employee employee1 = empty.orElse(new Employee());
//		Employee{name='null', age=null, salary=null, status=null}        
        System.out.println(employee1);
    }
orElseGet(Supplier s)

如果调用对象包含值,返回该值,否则s获取的值

    public static void main(String[] args) {
        Employee employee=null;
//        传入的为空
        Optional<Employee> empty = Optional.ofNullable(employee);
//        若为空 则使用传入的供给型Supplier接口。 否则使用ofNullable传入的。
        Employee employee1 = empty.orElseGet(()->new Employee());
        System.out.println(employee1);
    }SS
map(Function f)

如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()

    public static void main(String[] args) {
        Employee employee=new Employee("louchen",10,1000.0,Status.VOCATION);
        Optional<Employee> empty = Optional.ofNullable(employee);
//        获取元素的name属性
        Optional<String> s = empty.map((a) -> a.getName());
//        louchen
        System.out.println(s.get());
    }
flatMap(Function mapper)

与mapper类似,要求返回值必须是Optional

    public static void main(String[] args) {
        Employee employee=new Employee("louchen",10,1000.0,Status.VOCATION);
        Optional<Employee> empty = Optional.ofNullable(employee);
//        获取元素的name属性
        Optional<String> o = empty.flatMap((a) -> Optional.ofNullable(a.getName()));
//        louchen
        System.out.println(o.get());
    }

六、对日期的处理

1、LocalDateTime/LocalDate/LocalTime

LocalDateTime: 对时间日期操作

LocalDate:对日期操作

LocalTime:对时间操作

这里我们只介绍LocalDateTimeLocalDate/LocalTime用法和LocalDateTime一致。

//        1、LocalDateTime   时间日期操作
//        ①获取当前日期时间
        LocalDateTime ldt=LocalDateTime.now();
//        2020-05-11T19:25:27.520
        System.out.println(ldt);

//        ②根据年月日时分秒 生成指定的日期时间
        LocalDateTime ldt1=LocalDateTime.of(2020, 1, 31, 18, 11,12);
//        2020-01-31T18:11:12
        System.out.println(ldt1);

//        ③加上指定年份
        LocalDateTime ldt2=ldt.plusYears(2);
//        并格式化日期 2022-05-11 19:33:26
        System.out.println(ldt2.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

//        ④减上指定月份
        LocalDateTime ldt3=ldt.minusMonths(3);
//        2020/02/11 19:36:19
        System.out.println(ldt3.format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")));

//        ⑤分别获取 年月日时分秒
        System.out.println(ldt.getYear());
        System.out.println(ldt.getMonth());
        System.out.println(ldt.getDayOfMonth());
        System.out.println(ldt.getHour());
        System.out.println(ldt.getMinute());
        System.out.println(ldt.getSecond());

//        2、LocalTime  时间操作
//        3、LocalDate  日期操作
//        与LocalDateTime操作类似

2、Instant 时间戳

//        4、Instant: 时间戳(以Unix元年:1970年1月1日00:00:00到某个时间之间的毫秒数)
        Instant ins=Instant.now();
//        默认使用的是UTC时区的时间(本初子午线)。与中国时差为8小时
        System.out.println(ins);

//        修正为中国时区
        OffsetDateTime offsetDateTime = ins.atOffset(ZoneOffset.ofHours(8));
//        2020-05-11T20:12:35.134+08:00  即UTC时区加了8个小时
        System.out.println(offsetDateTime);

//        获取时间戳(毫秒) 当前时间与UTC 1970年1月1日午夜之间的时差(以毫秒为单位)
        System.out.println(ins.toEpochMilli());
//        与这个System中获取时间戳一致
        System.out.println(System.currentTimeMillis());

//        相对于UTC(1970-01-01)时间进行的时间累加操作
        Instant instant = Instant.ofEpochSecond(60);
//        1970-01-01T00:01:00Z
        System.out.println(instant);

3、Durartion/Period

Durartion: 计算两个 "时间" 之间的间隔 Period: 计算两个 "日期" 之间的间隔

//        ①查看时间间隔
        Instant ins1=Instant.now();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Instant ins2=Instant.now();
        Duration between = Duration.between(ins1, ins2);
//        查看相隔的秒数
        System.out.println(between.getSeconds());
//        查看相隔的毫秒数
        System.out.println(between.toMillis());

//        ②查看日期间隔
        LocalDate ld1 = LocalDate.of(2010, 10, 25);
        LocalDate ld2=LocalDate.now();
        Period between1 = Period.between(ld1, ld2);
//        P9Y6M16D
        System.out.println(between1);
//        分别获取相距的 年月日
        System.out.println(between1.getYears());
        System.out.println(between1.getMonths());
        System.out.println(between1.getDays());

4、TemporaAdjuster

时间校正器

//        当前日期
        LocalDateTime ldt4=LocalDateTime.now();
//       2020-05-11T22:26:27.874
        System.out.println(ldt4);
//        ①对已有的日期进行重新指定。对日指定为1
        LocalDateTime ldt5 = ldt4.withDayOfMonth(1);
//      2020-05-01T22:26:27.874
        System.out.println(ldt5);

//        ②从TemporalAdjusters工具类中获取其他指定日期
//        获取下个星期六的时间
        LocalDateTime with = ldt4.with(TemporalAdjusters.next(DayOfWeek.SATURDAY));
//        2020-05-16T22:31:10.829
        System.out.println(with);

//        ③计算下个工作日日期 (星期一到星期五为工作日)
        LocalDateTime ldt6= ldt4.with(t->{
//            获取当前日期对象
           LocalDateTime ldt7=(LocalDateTime)t;
//             获取该周的星期
            DayOfWeek dayOfMonth = ldt7.getDayOfWeek();
//            若为星期五
            if(dayOfMonth.equals(DayOfWeek.FRIDAY)){
//                则给当前日期加3天
                return ldt7.plusDays(3);
            }else if(dayOfMonth.equals(DayOfWeek.SATURDAY)){
//                若为星期六 加2天
                return ldt7.plusDays(2);
            }else{
//                其他加1天 即为下一个工作日
                return ldt7.plusDays(1);
            }
        });
        System.out.println(ldt6);

5、DateTimeFormatter

//        1、DateTimeFormatter 格式化日期
        LocalDateTime ldt=LocalDateTime.now();

//        ①LocalDateTime日期转换为自定义格式的日期字符串
        DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String format = ldt.format(dtf);
//       2020-05-12 09:58:04
        System.out.println(format);

//        ②将字符串日期格式化日期LocalDateTime
        LocalDateTime parse = LocalDateTime.parse(format, dtf);
//        2020-05-12T10:01:07
        System.out.println(parse);

6、Zone

//        2、带时区的日期时间
//        ①获取所有时区
        Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
//        Australia/Eucla
//        Asia/Shanghai
//        .......
//        availableZoneIds.forEach(System.out::println);

//        ②获取指定时区的时间
        LocalDateTime ldt1=LocalDateTime.now(ZoneId.of("Australia/Eucla"));
//        2020-05-12T10:57:50.461
        System.out.println(ldt1);

//        ③获取带 时区和时间日期
        LocalDateTime ldt2=LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        ZonedDateTime zonedDateTime = ldt2.atZone(ZoneId.of("Asia/Shanghai"));
//        2020-05-12T10:12:50.461+08:00[Asia/Shanghai]  上海与utc时差为8小时
        System.out.println(zonedDateTime);