1. 程式人生 > >java8 Lambda表示式和Stream Api

java8 Lambda表示式和Stream Api

兩個用於測試的類

員工:

public class Employee {

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public Employee(int id, String name, int age, double salary) {

        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public Employee() {

    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }

    public Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public Employee(int id) {

        this.id = id;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Employee employee = (Employee) o;

        if (id != employee.id) return false;
        if (age != employee.age) return false;
        if (Double.compare(employee.salary, salary) != 0) return false;
        return name != null ? name.equals(employee.name) : employee.name == null;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        result = 31 * result + age;
        temp = Double.doubleToLongBits(salary);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }
}
/**
 * 提供用於測試的資料
 *
 */
public class EmployeeData {
	
	public static List<Employee> getEmployees(){
		List<Employee> list = new ArrayList<>();
		
		list.add(new Employee(1001, "馬化騰", 34, 6000.38));
		list.add(new Employee(1002, "馬雲馬", 12, 9876.12));
		list.add(new Employee(1003, "劉強東", 33, 3000.82));
		list.add(new Employee(1004, "雷軍", 26, 7657.37));
		list.add(new Employee(1005, "李彥巨集", 65, 5555.32));
		list.add(new Employee(1006, "比爾蓋茨", 42, 9500.43));
		list.add(new Employee(1007, "任正非", 26, 4333.32));
		list.add(new Employee(1008, "爾扎克伯格", 35, 2500.32));
		
		return list;
	}
	
}

一、Lambda表示式的基本語法。

 1.格式:  lambda形參列表 -> lambda體  2.說明: -> : lambda操作符 或箭頭操作符          ->左邊 :lambda表示式的形參列表          ->右邊:lambda表示式的執行語句,稱為lambda體

 3.如何使用:分為六種情況

public class LambdaTest {

    //情況六:lambda體如果只有一條執行語句,可以省略這一對{}.
    //特別的,如果此唯一的一條執行語句是return,則除了省略一對{}之外,return關鍵字也可以省略
    @Test
    public void test7(){
        Comparator<Integer> com1 = (o1,o2) -> o1.compareTo(o2);
        int value = com1.compare(12, 34);
        System.out.println(value);
    }

    //情況五:lambda表示式的形參列表有兩個或兩個以上的變數,lambda體有多條執行語句,甚至有返回值
    @Test
    public void  test6(){
        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                System.out.println(o1);
                System.out.println(o2);
//                return Integer.compare(o1,o2);
                return o1.compareTo(o2);
            }
        };
        int value = com1.compare(12, 34);
        System.out.println(value);

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

        Comparator<Integer> com2 = (o1,o2) -> {
            System.out.println(o1);
            System.out.println(o2);
//          return Integer.compare(o1,o2);
            return o1.compareTo(o2);
        };

        int value1 = com2.compare(32, 12);
        System.out.println(value1);

    }

    //情況四:針對於情況三進行迭代。
    //如果lambda表示式的形參列表只有一個變數,則可以省略一對()
    @Test
    public void test5(){
        Consumer<String> con1 = s -> {
            System.out.println(s);
        };

        con1.accept("你好我也好!");
    }

    //以前在java程式中出現的型別推斷
    @Test
    public void test4(){
        //舉例1:
        int[] arr = new int[]{1,2,3,4};
        //型別推斷
        int[] arr1 = {1,2,3,4};
        int[] arr2 ;
        arr2 = new int[]{1,2,3,4};
        //舉例2:
        List<String> list = new ArrayList<String>();
        //型別推斷
        List<String> list1 = new ArrayList<>();

        //舉例3
        method(new HashMap<>());
    }
    public void method(HashMap<String,Employee> map){

    }


    //情況三:針對於情況二進行迭代。lambda形參列表的變數的資料型別可以省略
    //說明:java編譯器可以根據上下文推斷出變數的資料型別,故可以省略:型別推斷
    @Test
    public void test3(){
        Consumer<String> con1 = (s) -> {
            System.out.println(s);
        };

        con1.accept("你好我也好!");
    }

    //情況二:lambda形參列表有一個引數,無返回值
    @Test
    public void test2(){
        Consumer<String> con = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };

        con.accept("昨天過的挺好!");

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

        Consumer<String> con1 = (String s) -> {
            System.out.println(s);
        };

        con1.accept("你好我也好!");

    }


    //情況一:無形參,無返回值
    @Test
    public void test1(){
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("昨天是七夕情人節!");
            }
        };

        Thread t1 = new Thread(r);
        t1.start();

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

        Runnable r1 = () -> {
            System.out.println("你們有沒有因為愛情而鼓掌呢?");
        };

        Thread t2 = new Thread(r1);
        t2.start();
    }
}

 總結:  1)lambda形參列表:如果有形參的話,都可以省略變數的資料型別 ---型別推斷                    如果形參列表只有一個形參,還可以省略一對()  2)lambda體:正常情況下,需要使用一對{}包起來所有的執行語句。             特別的,①lambda體如果只有一條執行語句,可以省略這一對{}.                    ②如果此唯一的一條執行語句是return,則除了省略一對{}之外,return關鍵字也可以省略

  二、什麼是函式式介面

  1.特點:如果一個介面中只有唯一的一個抽象方法,則此介面稱為函式式介面   2.可以在介面的宣告上使用@FunctionalInterface註解去校驗一個介面是否是函式式介面   3.lambda表示式的使用依賴於函式式介面   4.lambda表示式即為函式式介面的例項。

@FunctionalInterface
public interface MyFunction {
    public String getValue(String str);
}

  只要一個物件是函式式介面的例項,那麼該物件就可以用Lambda表示式來表示。   所以以前用匿名類表示的現在都可以用Lambda表示式來寫。

    java8中關於Lambda表示式提供的4個基本的函式式介面  

  1.Consumer<T> :消費型介面        void accept(T t)     2.Supplier<T> : 供給型介面        T get()     3. Function<T,R> : 函式型介面        R apply(T t)     4. Predicate<T> : 斷定型介面        boolean test(T t)  

public class LambdaTest1 {

    //4. Predicate<T> : 斷定型介面
    //boolean test(T t)
    @Test
    public void test4(){
        List<String> list = Arrays.asList("北京","南京","東京","西京","普京","上海","深圳");
        List<String> data = getStrings(list, s -> s.contains("京"));
        System.out.println(data);
    }

    public List<String> getStrings(List<String> list, Predicate<String> pre){
        List<String> data = new ArrayList<>();
        for (String s : list) {
            if(pre.test(s)){
                data.add(s);
            }
        }
        return data;
    }


    //3. Function<T,R> : 函式型介面
    //R apply(T t)
    @Test
    public void test3(){
        strHandler("    hel   lo   ",str -> str.trim());

        strHandler("世界那麼大,我想去看看",str -> str.substring(2,5));
    }

    public void strHandler(String str, Function<String,String> func){
        String s = func.apply(str);
        System.out.println(s);
    }


//    2.Supplier<T> : 供給型介面
//    T get()
    @Test
    public void test2(){
        List<Double> list = getRandomValue(10, () -> Math.random() * 100);
        for (Double d : list) {
            System.out.println(d);
        }
    }

    public List<Double> getRandomValue(int num, Supplier<Double> sup){
        List<Double> list = new ArrayList<>();
        for(int i = 0;i < num;i++){
           list.add(sup.get());
        }
        return list;
    }

    //1.Consumer<T> :消費型介面
    //void accept(T t)
    @Test
    public void  test1(){
        happyNight(500, s -> {
            System.out.println("學習很辛苦,累了的話,可以去正規的足浴店放鬆一下。花費:" + s);
        });
    }

   

}

   總結:從方法的角度來說:    1.方法在呼叫時,如果發現方法的形參是一個函式型介面,那麼我們呼叫此方法時,可以使用lambda表示式    作為實參傳遞給此介面形參    2.方法定義時,如果需要一個定義介面,且此介面只有一個抽象方法,(說明此介面就是函式式介面),那麼    考慮是有有現成的函式式介面可用。如果有,則不需要我們再去定義。比如:FilterData 替換為Predicate

 構造器引用

  一、構造器引用   1.格式: 類名 :: new   2.要求:函式式介面中抽象方法的形參列表與構造器形參列表一致(型別相同,個數相同),          同時,抽象方法的返回值型別即為構造器所屬的類的型別。

public class ConstructorRefTest {

    @Test
    public void test4(){
        Function<Integer,String[]> func1 = (length) -> new String[length];
        String[] arr = func1.apply(10);
        System.out.println(arr.length);

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

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

    @Test
    public void test3(){
        BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);
        Employee emp = func1.apply(10, "Jim");
        System.out.println(emp);

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

        BiFunction<Integer,String,Employee> func2 = Employee::new;
        Employee emp1 = func2.apply(20, "Jim");
        System.out.println(emp1);
    }

    @Test
    public void test2(){
        Function<Integer,Employee> func1 = (id) -> new Employee(id);
        Employee emp1 = func1.apply(12);
        System.out.println(emp1);

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

        Function<Integer,Employee> func2 = Employee::new;
        Employee emp2 = func2.apply(12);
        System.out.println(emp2);
    }

    @Test
    public void test1(){
        Supplier<Employee> sup = () -> new Employee();
        Employee emp = sup.get();
        System.out.println(emp);

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

        Supplier<Employee> sup1 = Employee::new;
        Employee emp1 = sup1.get();
        System.out.println(emp1);
    }
}

方法引用的使用

  1.方法引用可以看做是Lambda表示式深層次的表達,或者可以理解為:方法引用就是Lambda表示式。     又因為Lambda表示式本身就是函式式介面的例項,進而方法引用也可以看做函式式介面的例項。     2.使用情境:   當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用!     3.要求:函式式介面中抽象方法的形參列表和返回值型別 要與 方法引用對應的方法的形參列表和返回值型別一致!     4.格式:類(或 物件) :: 方法名     5.分為如下的三種情況:     ① 物件 :: 例項方法     ② 類 :: 靜態方法     ③ 類 :: 例項方法  (有難度)     6.在滿足如上的第2條d的情況下,使用方法引用替換Lambda表示式。如果方法引用不熟悉,可以使用Lambda表示式。    

public class MethodRefTest {

    //情況三:類 :: 例項方法  (有難度)
    //注意:當函式式介面方法的第一個引數是需要引用方法的呼叫者,
    //並且第二個引數是需要引用方法的引數(或無引數)時:ClassName::methodName
    @Test
    public void test7(){
        Employee emp = new Employee(1001, "Jerry", 32, 23430);
        Function<Employee,String> func1 = (e) -> e.getName();
        String name = func1.apply(emp);
        System.out.println(name);

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

        Function<Employee,String> func2 = Employee::getName;
        String name1 = func2.apply(emp);
        System.out.println(name1);

    }
    @Test
    public void test6(){
        BiPredicate<String,String> bi = (s1,s2) -> s1.equals(s2);
        boolean b = bi.test("abc", "abc");
        System.out.println(b);

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

        BiPredicate<String,String> bi1 = String::equals;
        boolean b1 = bi1.test("abc", "abc");
        System.out.println(b1);
    }
    @Test
    public void test5(){
        Comparator<String> com = (s1,s2) -> s1.compareTo(s2);
        int value = com.compare("abad", "abdd");
        System.out.println(value);

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

        Comparator<String> com1 = String::compareTo;
        int value1 = com1.compare("aaaaaa", "aa");
        System.out.println(value1);
    }

    //情況二:類 :: 靜態方法
    @Test
    public void test4(){
        Function<Double,Long> func1 = d -> Math.round(d);
        Long value = func1.apply(12.3);
        System.out.println(value);

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

        Function<Double,Long> func2 = Math::round;
        Long value1 = func2.apply(12.6);
        System.out.println(value1);

        Function<Long,Long> func3 = Math::abs;
        Long value2 = func3.apply(-1234L);
        System.out.println(value2);
    }
    @Test
    public void test3(){
        Comparator<Integer> com = (num1,num2) -> Integer.compare(num1,num2);
        int value = com.compare(12, 32);
        System.out.println(value);

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

        Comparator<Integer> com1 = Integer::compare;
        int value1 = com1.compare(43, 12);
        System.out.println(value1);

    }

    //情況一:物件 :: 例項方法
    @Test
    public void test2(){
        Employee emp = new Employee(1001, "Tom", 23, 4534);
        Supplier<String> sup = () -> emp.getName();
        String name = sup.get();
        System.out.println(name);

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

        Supplier<String> sup1 = emp::getName;
        String name1 = sup1.get();
        System.out.println(name1);
    }
    @Test
    public void test1(){
        Consumer<String> con1 = str -> System.out.println(str);
        con1.accept("beijing");

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

        PrintStream ps = System.out;
        Consumer<String> con2 = ps :: println;
        con2.accept("shanghai");

    }
}

Stream API

  1.Stream API:     可以理解為java提供的一套api,使用這套api可以實現對集合、陣列中的資料進行過濾、對映、歸約、查詢等操作     2.注意點:   ①Stream 自己不會儲存元素。   ②Stream 不會改變源物件。相反,他們會返回一個持有結果的新Stream。   ③Stream 操作是延遲執行的。這意味著他們會等到需要結果的時候才執行。     3.Stream的使用流程:    步驟一:Stream的例項化    步驟二:一系列的中間操作    步驟三:終止操作      注意:①步驟二中的中間操作可以有多個         ②如果沒有終止操作,那麼一系列的中間操作是不會執行的。只有執行了步驟三的終止操作,步驟二才會執行:惰性求值         ③終止操作一旦執行,就不可以再執行中間操作或其他的終止操作。    

步驟一:Stream的例項化

public class StreamAPITest {

    //方式四:建立無限流
    @Test
    public void test4(){
//        迭代
//        public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
        Stream<Integer> stream = Stream.iterate(0, x -> x + 2);
        stream.limit(10).forEach(System.out::println);


//        生成
//        public static<T> Stream<T> generate(Supplier<T> s)
        Stream<Double> stream1 = Stream.generate(Math::random);
        stream1.limit(10).forEach(System.out::println);

    }

    //方式三:Stream的靜態方法of()
    @Test
    public void test3(){
        //public static<T> Stream<T> of(T... values) : 返回一個流
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
    }

    //方式二:通過陣列
    @Test
    public void test2(){
        //呼叫Arrays的static <T> Stream<T> stream(T[] array): 返回一個流
        String[] arr = new String[]{"MM","GG","JJ","DD"};
        Stream<String> stream = Arrays.stream(arr);


    }

    //方式一:通過集合
    @Test
    public void test1(){
//        default Stream<E> stream() : 返回一個順序流
        List<Employee> list = EmployeeData.getEmployees();
        Stream<Employee> stream = list.stream();
//        default Stream<E> parallelStream() : 返回一個並行流
        Stream<Employee> stream1 = list.parallelStream();


    }
}

步驟二:測試中間操作

  1.可以通過Stream的例項,執行多次中間操作   2.中間操作,只有在執行了終止操作以後才會執行。

public class StreamAPITest1 {

    //3-排序
    @Test
    public void test4(){
//        sorted()——自然排序
        List<Integer> list = Arrays.asList(23,43,454,32,1,2,5,5,-8);
        list.stream().sorted().forEach(System.out::println);

        //此時針對Employees進行排序:失敗。原因:Employee類沒有實現Comparable介面
//        List<Employee> list1 = EmployeeData.getEmployees();
//        list1.stream().sorted().forEach(System.out::println);

//        sorted(Comparator com)——定製排序
        List<Employee> list1 = EmployeeData.getEmployees();
        list1.stream().sorted((e1,e2) -> {
            if(e1.getAge() != e2.getAge()){
                return e1.getAge() - e2.getAge();
            }else{
                return -Double.compare(e1.getSalary(),e2.getSalary());
            }
        }).forEach(System.out::println);

    }

    @Test
    public void test3(){
        ArrayList list1 = new ArrayList();
        list1.add(1);
        list1.add(2);
        list1.add(3);

        ArrayList list2 = new ArrayList();
        list2.add(4);
        list2.add(5);
        list2.add(6);

//        list1.add(list2); ---map()
//        System.out.println(list1);//[1, 2, 3, [4, 5, 6]]

//        list1.addAll(list2); -- flatMap()
//        System.out.println(list1);//[1, 2, 3, 4, 5, 6]

    }
    //2-對映
    @Test
    public void test2(){
//        map(Function f)——接收一個函式作為引數,將元素轉換成其他形式或提取資訊,該函式會被應用到每個元素上,並將其對映成一個新的元素。
        List<String> list = Arrays.asList("aa","bb","cc","dd");
        list.stream().map(String::toUpperCase).forEach(System.out::println);
//        練習:獲取員工姓名長度大於3的員工的姓名。
        Stream<Employee> stream = EmployeeData.getEmployees().stream();
        Stream<String> stream1 = stream.map(Employee::getName);
        stream1.filter(name -> name.length() > 3).forEach(System.out::println);


        Stream<Stream<Character>> stream2 = list.stream().map(StreamAPITest1::fromStringToChar);
        stream2.forEach(
                x ->{
                   x.forEach(System.out::println);
                }

        );

        System.out.println();

//        flatMap(Function f)——接收一個函式作為引數,將流中的每個值都換成另一個流,然後把所有流連線成一個流。
        Stream<Character> stream3 = list.stream().flatMap(StreamAPITest1::fromStringToChar);
        stream3.forEach(System.out::println);
    }
    //將str中的字元存在集合中,返回集合的Stream
    public static Stream<Character> fromStringToChar(String str){
        ArrayList<Character> list = new ArrayList<>();
//        for(Character c : str.toCharArray()){
//              list.add(c);
//        }
        for(int i = 0;i < str.length();i++){
            list.add(str.charAt(i));
        }
        return list.stream();
    }


    //1-篩選與切片
    @Test
    public void test1(){
        List<Employee> list = EmployeeData.getEmployees();
        //體會方法鏈的呼叫方式。比如:StringBuffer s = new StringBuffer(); s.append("A").append("B");
//        filter(Predicate p)——接收 Lambda , 從流中排除某些元素。
        Stream<Employee> stream = list.stream().filter(e -> e.getAge() > 30);
        stream.forEach(System.out::println);

        System.out.println();

//        limit(n)——截斷流,使其元素不超過給定數量。
        list.stream().filter(e -> e.getAge() > 30).limit(3).forEach(System.out::println);

        System.out.println();

        //        skip(n) —— 跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素不足 n 個,則返回一個空流。與 limit(n) 互補
        list.stream().filter(e -> e.getAge() > 30).skip(3).forEach(System.out::println);

        System.out.println();

        //        distinct()——篩選,通過流所生成元素的 hashCode() 和 equals() 去除重複元素
        list.add(new Employee(1009,"劉強東",30,6000));
        list.add(new Employee(1009,"劉強東",30,6000));
        list.add(new Employee(1009,"劉強東",30,6000));
        list.add(new Employee(1009,"劉強東",30,6000));
        list.add(new Employee(1009,"劉強東",30,6000));
        list.stream().distinct().forEach(System.out::println);
    }
}

步驟三:終止操作

public class StreamAPITest2 {

    //3-收集:將集合--->Stream --->集合
    @Test
    public void test5(){
//        collect(Collector c)——將流轉換為其他形式。接收一個 Collector介面的實現,
//                              用於給Stream中元素做彙總的方法

        List<Employee> list = EmployeeData.getEmployees();
        List<Employee> list1 = list.stream().filter(e -> e.getSalary() > 5000).collect(Collectors.toList());
        //遍歷
        list1.forEach(System.out::println);

        System.out.println();

        Set<Employee> set = list.stream().filter(e -> e.getSalary() > 5000).collect(Collectors.toSet());
        set.forEach(System.out::println);

        System.out.println();

        ArrayList<Employee> list2 = list.stream().filter(e -> e.getSalary() > 5000).collect(Collectors.toCollection(ArrayList::new));
        for (Employee employee : list2) {
            System.out.println(employee);
        }
    }

    //2-歸約
    @Test
    public void test4(){
//        reduce(T identity, BinaryOperator)——可以將流中元素反覆結合起來,得到一個值。返回 T
        List<Integer> list = Arrays.asList(1,2,3,4,5,6);
//        Integer sum = list.stream().reduce(0, (x1, x2) -> x1 + x2);
        Integer sum = list.stream().reduce(10, Integer::sum);
        System.out.println(sum);


//        reduce(BinaryOperator) ——可以將流中元素反覆結合起來,得到一個值。返回 Optional<T>
//        練習1:計算公司所有員工工資的總和
        List<Employee> emps = EmployeeData.getEmployees();
        Stream<Double> moneyStream = emps.stream().map(Employee::getSalary);
        Optional<Double> moneyOptional = moneyStream.reduce(Double::sum);
        System.out.println(moneyOptional.get());




//        練習2:員工姓名中包含“馬”字的個數
        Stream<String> nameStream = emps.stream().map(Employee::getName);
        Stream<Character> charStream = nameStream.flatMap(StreamAPITest1::fromStringToChar);
        //方式一:
//        long count = charStream.filter(c -> c.equals('馬')).count();
//        System.out.println(count);
        //方式二:
        Optional<Integer> op = charStream.map(c -> {
            if (c.equals('馬')) {
                return 1;
            } else {
                return 0;
            }
        }).reduce(Integer::sum);

        System.out.println(op.get());

        //練習3:員工姓名中包含“馬”的員工個數
        long count = emps.stream().map(Employee::getName).filter(name -> name.contains("馬")).count();
        System.out.println(count);

//        練習4:員工姓名中包含“馬”的員工的姓名
        emps.stream().map(Employee::getName).filter(name -> name.contains("馬")).forEach(System.out::println);

    }
    //1-匹配與查詢
    @Test
    public void test2(){
        List<Employee> list = EmployeeData.getEmployees();
//        max(Comparator c)——返回流中最大值
//        練習:返回最高的工資:
        Stream<Employee> stream = list.stream();
        Stream<Double> stream1 = stream.map(Employee::getSalary);
        Optional<Double> max = stream1.max(Double::compare);
        System.out.println(max.get());
//        min(Comparator c)——返回流中最小值
//        練習:返回最低工資的員工
        Stream<Employee> stream2 = list.stream();
        Optional<Employee> min = stream2.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(min.get());
//        forEach(Consumer c)——內部迭代
        list.stream().forEach(System.out::println);

    }

    //外部迭代
    @Test
    public void test3(){
        List<Employee> list = EmployeeData.getEmployees();
        Iterator<Employee> iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

    @Test
    public void test1(){
        List<Employee> list = EmployeeData.getEmployees();
//        allMatch(Predicate p)——檢查是否匹配所有元素
        //是否所有的員工的年齡都大於18
        boolean b = list.stream().allMatch(e -> e.getAge() > 18);
        System.out.println(b);

//        anyMatch(Predicate p)——檢查是否至少匹配一個元素
        //是否存在員工的工資大於 10000
        boolean b1 = list.stream().anyMatch(e -> e.getSalary() > 9900);
        System.out.println(b1);

//        noneMatch(Predicate p)——檢查是否沒有匹配的元素
        //是否存在員工姓“雷”
        boolean b2 = list.stream().noneMatch(e -> e.getName().contains("雷"));
        System.out.println(b2);

//        findFirst——返回第一個元素
        Optional<Employee> emp = list.stream().sorted((e1,e2) -> {
            if(e1.getAge() != e2.getAge()){
                return e1.getAge() - e2.getAge();
            }else{
                return -Double.compare(e1.getSalary(),e2.getSalary());
            }
        }).findFirst();
        System.out.println(emp.get());

//        findAny——返回當前流中的任意元素
        Optional<Employee> emp1 = list.parallelStream().findAny();
        System.out.println(emp1.get());
//        count——返回流中元素的總個數
        long count = list.stream().filter(e -> e.getSalary() > 5000).count();
        System.out.println(count);


    }
}