1. 程式人生 > 實用技巧 >Java8特性總結——恕我直言你可能真的不會java系列學習筆記

Java8特性總結——恕我直言你可能真的不會java系列學習筆記

Java8特性總結

目錄

——恕我直言你可能真的不會java系列學習筆記

字母哥講:恕我直言你可能真的不會java系列的學習筆記,以下是學習資料直達地址
視訊:https://www.bilibili.com/video/BV1sE411P7C1


文章:https://www.kancloud.cn/hanxt/javacrazy/1568811
主講: java8基礎特性
恕我直言你可能真的不會java系列-lambda、streamAPI、文字塊等特性深入講解
學習筆記原始碼:https://github.com/ManMian/lambda-StreamAPI

01.lambda表示式會用了麼

lambda表示式表達介面函式的實現

一、介面定義

1、經典OOP的實現樣式
public class LambdaDemo {
    //函式定義
    public void printSomething(String something) {
        System.out.println(something);
    }
    //通過建立物件呼叫函式
    public static void main(String[] args) {
        LambdaDemo demo = new LambdaDemo();
        String something = "I am learning Lambda";
        demo.printSomething(something);
    }
}
2、建立功能介面,並對該介面定義抽象方法
public class LambdaDemo {
    //抽象功能介面
    interface Printer {
        void print(String val);
    }
    //通過引數傳遞功能介面
    public void printSomething(String something, Printer printer) {
        printer.print(something);
    }
}

二、傳統的介面函式實現方式

public static void main(String[] args) {
    LambdaDemo demo = new LambdaDemo();
    String something = "I am using a Functional interface";
    //實現Printer介面
    Printer printer = new Printer() {
        @Override
        public void print(String val) {
            //控制檯列印
            System.out.println(val);
        }
    };
    demo.printSomething(something, printer);
}

三、lambda表示式實現方式

lambda表示式的語法:

(param1,param2,param3 ...,paramN)-  > {   //程式碼塊;  }

首先我們知道lambda表示式,表達的是介面函式
箭頭左側是函式的逗號分隔的形式引數列表
箭頭右側是函式體程式碼

public static void main(String[] args) {
    LambdaDemo demo = new LambdaDemo();
    String something = "I am learning Lambda";
    //實現Printer介面(請關注下面這行lambda表示式程式碼)
    Printer printer = (String toPrint)->{System.out.println(toPrint);};
    //呼叫介面列印
    demo.printSomething(something, printer);
}

例子

package main.java;

public class LambdaDemo1 {
    interface Printer{
        void printer(String val);
    }
    public void pringSomething(String something,Printer printer){
        printer.printer(something);
    }

    public static void main(String[] args) {
        LambdaDemo1 lambdaDemo1 = new LambdaDemo1();
        String some = "asdasasa";

        //不使用lambda表示式
        Printer printer = new Printer() {
            @Override
            public void printer(String val) {
                System.out.println(val);
            }
        };
        lambdaDemo1.pringSomething(some,printer);

        /*1、使用lambda表示式
        *介面匿名實現類,簡化函式
        * ①引數
        * ②函式體
        * */
        Printer printer1 = (String val) ->{
            System.out.println(val);
        };

        //1.1、進一步簡化,去掉引數型別
        Printer printer2 = (val) ->{
            System.out.println(val);
        };
        //1.2、進一步簡化,去掉引數括號(前提:只有一個引數)
        Printer printer3 = val ->{
            System.out.println(val);
        };
        //1.3、進一步簡化,去掉函式體大括號(前提:函式體只有一行程式碼)
        Printer printer4 = val -> System.out.println(val);

        //1.4、最後可精簡為如下,會自動推斷lambda傳入的引數型別
        lambdaDemo1.pringSomething(some,val -> System.out.println(val));

        //1.5、無引數傳入的話,可以簡寫成
//        () -> System.out.println("無參");
        /*
        * 總結:
        * 省略型別:自動推斷
        * 省略括號:一個引數
        * 
        * lambda表示式表達的是介面函式,
        * 箭頭左側是函式引數,箭頭右側是函式體。
        * 函式的引數型別和返回值型別都可以省略,程式會根據介面定義的上下文自動確定資料型別。
        * */
    }
}

02.Stream代替for迴圈(初識Stream-API)

認識

Java Stream就是一個數據流經的管道,並且在管道中對資料進行操作,然後流入下一個管道。

管道的功能包括:Filter(過濾)、Map(對映)、sort(排序)等,集合資料通過Java Stream管道處理之後,轉化為另一組集合或資料輸出。

例子 Stream代替for迴圈

package main.java;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamDemo1 {
    public static void main(String[] args) {
        List<String> letters = Arrays.asList("e", "c", "cbn", "zxc", "caz", "d", "b", "a");

        //不使用StreamAPI
       /* for (String letter : letters) {
                                //轉換大寫
            String temp = letter.toLowerCase();
            //排序
            //轉
            //總之非常麻煩
        }*/

        //1、集合 使用StreamAPI
        //陣列轉換成流
        List<String> lettertsNew = letters.stream()
                //過濾:找出以c開頭的元素
                .filter(s -> s.startsWith("c"))
                //將過濾後的結果全部大寫,其中::為函式引用
                .map(String::toUpperCase)
                //排序,可寫排序規則
                .sorted()
                //將流轉換成集合
                .collect(Collectors.toList());

        System.out.println(lettertsNew);

        //2、陣列轉換成流
        String[] letters2 = {"e", "c", "cbn", "zxc", "caz", "d", "b", "a"};
        Stream.of(letters2).filter(s -> s.startsWith("c")).map(String::toUpperCase).sorted().toArray(String[]::new);

        //3、set轉成流:集合類是一樣的
        Set<String> lettersSet = new HashSet<>(letters);
        List<String> lettertsNewSet = lettersSet.stream()
                //過濾:找出以c開頭的元素
                .filter(s -> s.startsWith("c"))
                //將過濾後的結果全部大寫,其中::為函式引用
                .map(String::toUpperCase)
                //排序,可寫排序規則
                .sorted()
                //將流轉換成集合
                .collect(Collectors.toList());

        //4、文字檔案轉換成流
        Paths.get("file.text");
        try {
            Stream<String> stringStream = Files.lines(Paths.get("file.text"));
            List<String> f = stringStream
                    //過濾:找出以c開頭的元素
                    .filter(s -> s.startsWith("c"))
                    //將過濾後的結果全部大寫,其中::為函式引用
                    .map(String::toUpperCase)
                    //排序,可寫排序規則
                    .sorted()
                    //將流轉換成集合
                    .collect(Collectors.toList());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

題外話 介紹雙冒號操作符

雙冒號運算就是Java中的[方法引用] Method

[方法引用]的格式是

類名::方法名

例如

1.表示式:
person -> person.getName();
可以替換成:
Person::getName

2.表示式:
() -> new HashMap<>();
可以替換成:
HashMap::new

方法引用的種類

方法引用有四種,分別是:

指向靜態方法的引用

指向某個物件的例項方法的引用

指向某個型別的例項方法的引用

指向構造方法的引用

其實,JVM 本身並不支援指向方法引用,過去不支援,現在也不支援。

Java 8 對方法引用的支援只是編譯器層面的支援,虛擬機器執行引擎並不瞭解方法引用。編譯器遇到方法引用的時候,會像上面那樣自動推斷出程式設計師的意圖,將方法引用還原成介面實現物件,或者更形象地說,就是把方法引用設法包裝成一個介面實現物件,這樣虛擬機器就可以無差別地執行位元組碼檔案而不需要管
什麼是方法引用了。

需要注意的是,方法引用是用來簡化介面實現程式碼的,並且凡是能夠用方法引用來簡化的介面,都有這樣的特徵:有且只有一個待實現的方法。這種介面在 Java 中有個專門的名稱: 函式式介面。當你用試圖用方法引用替代一個非函式式介面時,會有這樣的錯誤提示: xxx is not a functional interface。

3.Stream的filter與謂語邏輯

什麼是謂詞邏輯?

WHERE 和 AND 限定了主語employee是什麼,那麼WHERE和AND語句所代表的邏輯就是謂詞邏輯

SELECT *
FROM employee
WHERE age > 70
AND gender = 'M'

謂詞邏輯的複用

filter函式中lambda表示式為一次性使用的謂詞邏輯。如果我們的謂詞邏輯需要被多處、多場景、多程式碼中使用,通常將它抽取出來單獨定義到它所限定的主語實體中。
比如:將下面的謂詞邏輯定義在Employee實體class中。

 public static Predicate<Employee> ageGreaterThan70 = x -> x.getAge() >70;
   public static Predicate<Employee> genderM = x -> x.getGender().equals("M");

and語法(並集)
or語法(交集)
negate語法(取反)

例子

package model;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.function.Predicate;


@Data
@AllArgsConstructor
//員工
public class Employee {

    private Integer id;
    private Integer age;   //年齡
    private String gender;  //性別
    private String firstName; //名字
    private String lastName; //姓氏

    //Predicate介面,在英語中這個單詞的意思是:謂詞
    //謂詞邏輯的複用如下
    //e -> e.getAge() > 70 && e.getGender().equals("M")
    public static Predicate<Employee> ageGreaterThan70 = x -> x.getAge() >70;
    public static Predicate<Employee> genderM = x -> x.getGender().equals("M");
}
package model;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamFilterPredicate {
    
    public static void main(String[] args){
        Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
        Employee e2 = new Employee(2,13,"F","Martina","Hengis");
        Employee e3 = new Employee(3,43,"M","Ricky","Martin");
        Employee e4 = new Employee(4,26,"M","Jon","Lowman");
        Employee e5 = new Employee(5,19,"F","Cristine","Maria");
        Employee e6 = new Employee(6,15,"M","David","Feezor");
        Employee e7 = new Employee(7,68,"F","Melissa","Roy");
        Employee e8 = new Employee(8,79,"M","Alex","Gussin");
        Employee e9 = new Employee(9,15,"F","Neetu","Singh");
        Employee e10 = new Employee(10,45,"M","Naveen","Jain");


        List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);

        List<Employee> filtered = employees.stream()
                //filter(寫謂詞邏輯) 年齡大於70 並且是男性
                .filter(e -> e.getAge() > 70 && e.getGender().equals("M"))
                .collect(Collectors.toList());

        //使用可複用謂詞邏輯
        List<Employee> filteredAnd = employees.stream()
                .filter(Employee.ageGreaterThan70
                        .and(Employee.genderM))
                .collect(Collectors.toList());

        //or
        List<Employee> filteredOr = employees.stream()
                .filter(Employee.ageGreaterThan70
                        .or(Employee.genderM))
                .collect(Collectors.toList());

        //negate 取反
        List<Employee> filteredNegate = employees.stream()
                .filter(
                        Employee.ageGreaterThan70
                                .or(Employee.genderM)
                                .negate()
                )
                .collect(Collectors.toList());


        System.out.println("filtered="+filtered);
        System.out.println("filteredAnd="+filteredAnd);
        System.out.println("filteredOr="+filteredOr);
        System.out.println("filteredNegate="+filteredNegate);
    }

}

4.Stream的map資料轉換(Stream管道流的map操作)

map函式的作用就是針對管道流中的每一個數據元素進行轉換操作。

例子


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

public class StreamMap1 {
    public static void main(String[] args) {
        List<String> alpha = Arrays.asList("Monkey", "Lion", "Giraffe", "Lemur");

        //不使用Stream管道流
        List<String> alphaUpper = new ArrayList<>();
        for (String s : alpha) {
            alphaUpper.add(s.toUpperCase());
        }
        System.out.println(alphaUpper); //[MONKEY, LION, GIRAFFE, LEMUR]

        // 1、Stream管道流map的基礎用法:使用Stream管道流
        List<String> collect = alpha.stream().map(String::toUpperCase).collect(Collectors.toList());
        //上面使用了方法引用,和下面的lambda表示式語法效果是一樣的
        //List<String> collect = alpha.stream().map(s -> s.toUpperCase()).collect(Collectors.toList());

        System.out.println(collect); //[MONKEY, LION, GIRAFFE, LEMUR]

        //2、處理非字串型別集合元素
        List<Integer> lengths = alpha.stream()
                .map(String::length)
                .collect(Collectors.toList());

        System.out.println(lengths); //[6, 4, 7, 5]

        Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
                //除了mapToInt。還有maoToLong,mapToDouble等等用法
                .mapToInt(String::length)
                .forEach(System.out::println);
    }
}

import model.Employee;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamMap2 {
    public static void main(String[] args) {
        Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
        Employee e2 = new Employee(2,13,"F","Martina","Hengis");
        Employee e3 = new Employee(3,43,"M","Ricky","Martin");
        Employee e4 = new Employee(4,26,"M","Jon","Lowman");
        Employee e5 = new Employee(5,19,"F","Cristine","Maria");
        Employee e6 = new Employee(6,15,"M","David","Feezor");
        Employee e7 = new Employee(7,68,"F","Melissa","Roy");
        Employee e8 = new Employee(8,79,"M","Alex","Gussin");
        Employee e9 = new Employee(9,15,"F","Neetu","Singh");
        Employee e10 = new Employee(10,45,"M","Naveen","Jain");


        List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);

        //每人漲一歲,性別換全詞
        /*List<Employee> maped = employees.stream()
            .map(e -> {
                e.setAge(e.getAge() + 1);
                e.setGender(e.getGender().equals("M")?"male":"female");
                return e;
            }).collect(Collectors.toList());*/

        List<Employee> maped = employees.stream()
                .peek(e -> {
                    e.setAge(e.getAge() + 1);
                    e.setGender(e.getGender().equals("M")?"male":"female");
                }).collect(Collectors.toList());

        System.out.println(maped);
    }
}

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

public class StreamFlatMap {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("hello", "word");
        words.stream()
                .map(w -> Arrays.stream(w.split("")))    //[[h,e,l,l,o],[w,o,r,l,d]]
                .forEach(System.out::println);

        //flatMap可以理解為將若干個子管道中的資料全都,平面展開到父管道中進行處理
        words.stream()
                .flatMap(w -> Arrays.stream(w.split(""))) // [h,e,l,l,o,w,o,r,l,d]
                .forEach(System.out::println);
    }
}

5.Stream的狀態與並行操作

Stream管道流的基本操作

  1. 源操作:可以將陣列、集合類、行文字檔案轉換成管道流Stream進行資料處理
  2. 中間操作:對Stream流中的資料進行處理,比如:過濾、資料轉換等等
  3. 終端操作:作用就是將Stream管道流轉換為其他的資料型別。

中間操作:有狀態與無狀態

狀態通常代表公用資料,有狀態就是有“公用資料”

Limit與Skip管道資料擷取
Distinct元素去重
Sorted排序
序列、並行與順序

例子

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamState {
    public static void main(String[] args) {
        //1、Limit與Skip管道資料擷取
        List<String> limitN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
                .limit(2)
                .collect(Collectors.toList());
        List<String> skipN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
                .skip(2)
                .collect(Collectors.toList());
        //2、Distinct元素去重
        List<String> uniqueAnimals = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                .distinct()
                .collect(Collectors.toList());
        //3、Sorted排序
        List<String> alphabeticOrder = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
                .sorted()
                .collect(Collectors.toList());
        //4、序列、並行與順序
        //預設序列
        //並行操作parallel,操作無狀態操作
        Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                .parallel()
                .forEach(System.out::println);
    }
}

結果

Giraffe
Lion
Lemur
Lion
Monkey

Process finished with exit code 0

6.Stream效能差問號 不要人云亦云

7.像使用SQL一樣排序集合

一、字串List排序
二、整數型別List排序
三、按物件欄位對List排序
四、Comparator鏈對List排序

例子

import model.Employee;

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

public class SortList {

    public static void main(String[] args) {

        //Collections.sort();
        //1、字串List排序
        List<String> cities = Arrays.asList(
                "Milan",
                "london",
                "San Francisco",
                "Tokyo",
                "New Delhi"
        );
        System.out.println(cities);
        //[Milan, london, San Francisco, Tokyo, New Delhi]

        //大小寫不敏感的排序
        cities.sort(String.CASE_INSENSITIVE_ORDER);
        System.out.println(cities);
        //[london, Milan, New Delhi, San Francisco, Tokyo]

        //大小寫敏感的排序
        cities.sort(Comparator.naturalOrder());
        System.out.println(cities);
        //[Milan, New Delhi, San Francisco, Tokyo, london]

        //同樣我們可以把排序器Comparator用在Stream管道流中。
        cities.stream().sorted(Comparator.naturalOrder()).forEach(System.out::println);
        //Milan
        //New Delhi
        //San Francisco
        //Tokyo
        //london

        //2、整數型別List排序
        List<Integer> numbers = Arrays.asList(6, 2, 1, 4, 9);
        System.out.println(numbers); //[6, 2, 1, 4, 9]

        numbers.sort(Comparator.naturalOrder());  //自然排序
        System.out.println(numbers); //[1, 2, 4, 6, 9]

        numbers.sort(Comparator.reverseOrder()); //倒序排序
        System.out.println(numbers);  //[9, 6, 4, 2, 1]

        //3、按物件欄位對List<Object>排序
        Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
        Employee e2 = new Employee(2,13,"F","Martina","Hengis");
        Employee e3 = new Employee(3,43,"M","Ricky","Martin");
        Employee e4 = new Employee(4,26,"M","Jon","Lowman");
        Employee e5 = new Employee(5,19,"F","Cristine","Maria");
        Employee e6 = new Employee(6,15,"M","David","Feezor");
        Employee e7 = new Employee(7,68,"F","Melissa","Roy");
        Employee e8 = new Employee(8,79,"M","Alex","Gussin");
        Employee e9 = new Employee(9,15,"F","Neetu","Singh");
        Employee e10 = new Employee(10,45,"M","Naveen","Jain");

        List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);


        //4、Comparator鏈對List<Object>排序
        employees.sort(Comparator.comparing(Employee::getAge));
        employees.forEach(System.out::println);

        //先按性別,再按年齡倒序
        //如果我們希望List按照年齡age的倒序排序,就使用reversed()方法
        employees.sort(
                Comparator.comparing(Employee::getGender)
                        .thenComparing(Employee::getAge)
                        .reversed()
        );
        employees.forEach(System.out::println);

        //都是正序 ,不加reversed
        //都是倒序,最後面加一個reserved
        //先是倒序(加reserved),然後正序
        //先是正序(加reserved),然後倒序(加reserved)
    }
}

結果

[Milan, london, San Francisco, Tokyo, New Delhi]
[london, Milan, New Delhi, San Francisco, Tokyo]
[Milan, New Delhi, San Francisco, Tokyo, london]
Milan
New Delhi
San Francisco
Tokyo
london
[6, 2, 1, 4, 9]
[1, 2, 4, 6, 9]
[9, 6, 4, 2, 1]
Employee(id=8, age=79, gender=M, firstName=Alex, lastName=Gussin)
Employee(id=10, age=45, gender=M, firstName=Naveen, lastName=Jain)
Employee(id=3, age=43, gender=M, firstName=Ricky, lastName=Martin)
Employee(id=4, age=26, gender=M, firstName=Jon, lastName=Lowman)
Employee(id=1, age=23, gender=M, firstName=Rick, lastName=Beethovan)
Employee(id=6, age=15, gender=M, firstName=David, lastName=Feezor)
Employee(id=7, age=68, gender=F, firstName=Melissa, lastName=Roy)
Employee(id=5, age=19, gender=F, firstName=Cristine, lastName=Maria)
Employee(id=9, age=15, gender=F, firstName=Neetu, lastName=Singh)
Employee(id=2, age=13, gender=F, firstName=Martina, lastName=Hengis)

Process finished with exit code 0

8.函式式介面Comparator

含義

所謂的函式式介面,實際上就是接口裡面只能有一個抽象方法的介面。

二、函式式介面的特點

  • 介面有且僅有一個抽象方法,如上圖的抽象方法compare
  • 允許定義靜態非抽象方法
  • 允許定義預設defalut非抽象方法(default方法也是java8才有的,見下文)
  • 允許java.lang.Object中的public方法,如上圖的方法equals。
  • FunctionInterface註解不是必須的,如果一個介面符合"函式式介面"定義,那麼加不加該註解都沒有影響。加上該註解能夠更好地讓編譯器進行檢查。如果編寫的不是函式式介面,但是加上了@FunctionInterface,那麼編譯器會報錯

甚至可以說:函式式介面是專門為lambda表示式準備的,lambda表示式是隻實現介面中唯一的抽象方法的匿名實現類。

三、default關鍵字

順便講一下default關鍵字,在java8之前

介面是不能有方法的實現,所有方法全都是抽象方法
實現介面就必須實現接口裡面的所有方法

這就導致一個問題:當一個介面有很多的實現類的時候,修改這個介面就變成了一個非常麻煩的事,需要修改這個介面的所有實現類。

這個問題困擾了java工程師許久,不過在java8中這個問題得到了解決,沒錯就是default方法

default方法可以有自己的預設實現,即有方法體。
介面實現類可以不去實現default方法,並且可以使用default方法。

四、JDK中的函式式介面舉例

java.lang.Runnable,
java.util.Comparator,
java.util.concurrent.Callable
java.util.function包下的介面,如Consumer、Predicate、Supplier等

五、自定義Comparator排序

 //8、自定義Comparator排序
       /* employees.sort(new Comparator<Employee>() {
            @Override
            public int compare(Employee em1, Employee em2) {
                if(em1.getAge() == em2.getAge()){
                    return 0;
                }
                return em1.getAge() - em2.getAge() > 0 ? -1:1;
            }
        });*/
        //簡化
        employees.sort((em1,em2) -> {
            if(em1.getAge() == em2.getAge()){
                return 0;
            }
            return em1.getAge() - em2.getAge() > 0 ? -1:1;
        });
        employees.forEach(System.out::println);

9.Stream查詢與匹配元素

問題

在我們對陣列或者集合類進行操作的時候,經常會遇到這樣的需求,比如:
是否包含某一個“匹配規則”的元素
是否所有的元素都符合某一個“匹配規則”
是否所有元素都不符合某一個“匹配規則”
查詢第一個符合“匹配規則”的元素
查詢任意一個符合“匹配規則”的元素

例子

import model.Employee;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class Matvhfind {
    public static void main(String[] args) {
        Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
        Employee e2 = new Employee(2,13,"F","Martina","Hengis");
        Employee e3 = new Employee(3,43,"M","Ricky","Martin");
        Employee e4 = new Employee(4,26,"M","Jon","Lowman");
        Employee e5 = new Employee(5,19,"F","Cristine","Maria");
        Employee e6 = new Employee(6,15,"M","David","Feezor");
        Employee e7 = new Employee(7,68,"F","Melissa","Roy");
        Employee e8 = new Employee(8,79,"M","Alex","Gussin");
        Employee e9 = new Employee(9,15,"F","Neetu","Singh");
        Employee e10 = new Employee(10,45,"M","Naveen","Jain");

        List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);

        //不用Stream API實現,查詢員工列表中是否包含年齡大於70的員工
        boolean isExistAgeThan70 = false;
        for(Employee employee:employees){
            if(employee.getAge() > 70){
                isExistAgeThan70 = true;
                break;
            }
        }
        System.out.println(isExistAgeThan70);

        //1、第一個匹配規則函式:anyMatch,判斷Stream流中是否 包含 某一個“匹配規則”的元素。
        // 這個匹配規則可以是lambda表示式或者謂詞。

        //使用Stream API
        boolean isExistAgeThan702 = employees.stream().anyMatch(Employee.ageGreaterThan70);
        System.out.println(isExistAgeThan702);
        //將謂詞邏輯換成lambda表示式
        boolean isExistAgeThan72 = employees.stream().anyMatch(e -> e.getAge() > 72);
        System.out.println(isExistAgeThan72);

        //2.1、allMatch匹配規則函式:判斷是夠Stream流中的 所有元素都 符合某一個"匹配規則"。
        //是否所有員工的年齡都大於10歲
        boolean isExistAgeThan10 = employees.stream().allMatch(e -> e.getAge() > 10);
        System.out.println(isExistAgeThan10);

        //2.2、noneMatch匹配規則函式:判斷是否Stream流中的 所有元素都不 符合某一個"匹配規則"。
        //是否不存在小於18歲的員工
        boolean isExistAgeLess18 = employees.stream().noneMatch(e -> e.getAge() < 18);
        System.out.println(isExistAgeLess18);

        /**
         *  3、元素查詢與Optional
         * Optional類代表一個值存在或者不存在。在java8中引入,這樣就不用返回null了。
         *
         * isPresent() 將在 Optional 包含值的時候返回 true , 否則返回 false 。
         * ifPresent(Consumer block) 會在值存在的時候執行給定的程式碼塊。我們在第3章
         * 介紹了 Consumer 函式式介面;它讓你傳遞一個接收 T 型別引數,並返回 void 的Lambda
         * 表示式。
         * T get() 會在值存在時返回值,否則?出一個 NoSuchElement 異常。
         * T orElse(T other) 會在值存在時返回值,否則返回一個預設值。
         * 關於Optinal的各種函式用法請觀看視訊!B站觀看地址
         *
         * findFirst用於查詢第一個符合“匹配規則”的元素,返回值為Optional
         * findAny用於查詢任意一個符合“匹配規則”的元素,返回值為Optional
         */
        //從列表中按照順序查詢第一個年齡大於40的員工
        Optional<Employee> employeeOptional
                =  employees.stream().filter(e -> e.getAge() > 40).findFirst();
        System.out.println(employeeOptional.get());

        //isPresent是否存在
       boolean is
                =  employees.stream().filter(e -> e.getAge() > 40).findFirst().isPresent();
        System.out.println(is);

        //ifPresent如果存在
        employees.stream().filter(e -> e.getAge() > 40).findFirst().ifPresent(
                        e -> System.out.println(e)
        );

        //orElse不存在給預設值
        Employee employeeOptionalOrElse =
        employees.stream().filter(e -> e.getAge() > 90).findFirst().orElse(
                        new Employee(0,0,"F","","")
        );
        System.out.println(employeeOptionalOrElse);

        //findAny 找任意一個
        Employee employeeOptionalOrElseFindAny =
                employees.stream().filter(e -> e.getAge() > 90).findAny().orElse(
                        new Employee(0,0,"F","","")
                );
        System.out.println(employeeOptionalOrElseFindAny);
    }
}

結果

true
true
true
true
false
Employee(id=3, age=43, gender=M, firstName=Ricky, lastName=Martin)
true
Employee(id=3, age=43, gender=M, firstName=Ricky, lastName=Martin)
Employee(id=0, age=0, gender=F, firstName=, lastName=)
Employee(id=0, age=0, gender=F, firstName=, lastName=)

Process finished with exit code 0

10.Stream集合元素歸約

Stream API為我們提供了Stream.reduce用來實現集合元素的歸約。reduce函式有三個引數:

  • Identity標識:一個元素,它是歸約操作的初始值,如果流為空,則為預設結果。
  • Accumulator累加器:具有兩個引數的函式:歸約運算的部分結果和流的下一個元素。
  • Combiner合併器(可選):當歸約並行化時,或當累加器引數的型別與累加器實現的型別不匹配時,用於合併歸約操作的部分結果的函式。

注意觀察上面的圖,我們先來理解累加器:

  • 階段累加結果作為累加器的第一個引數
  • 集合遍歷元素作為累加器的第二個引數

Integer型別歸約
String型別歸約
複雜物件歸約
Combiner合併器的使用
對於大資料量的集合元素歸約計算,更能體現出Stream並行流計算的威力。

例子

import model.Employee;

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

/**
 * Stream API為我們提供了Stream.reduce用來實現集合元素的歸約。reduce函式有三個引數:
 *
 * Identity標識:一個元素,它是歸約操作的初始值,如果流為空,則為預設結果。
 * Accumulator累加器:具有兩個引數的函式:歸約運算的部分結果和流的下一個元素。
 * Combiner合併器(可選):當歸約並行化時,或當累加器引數的型別與累加器實現的型別不匹配時,用於合併歸約操作的部分結果的函式。
 * 理解累加器:
 * 階段累加結果作為累加器的第一個引數
 * 集合遍歷元素作為累加器的第二個引數
 */
public class MatchFindDemo {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

        //1、Integer型別歸約
        //reduce初始值為0,累加器可以是lambda表示式,也可以是方法引用。
        int result = numbers
                .stream()
                .reduce(0, (subtotal, element) -> subtotal + element);
        System.out.println(result);  //21

        int result12 = numbers
                .stream()
                .reduce(0, Integer::sum);
        System.out.println(result12); //21

        //2、String型別歸約
        //不僅可以歸約Integer型別,只要累加器引數型別能夠匹配,可以對任何型別的集合進行歸約計算。
        List<String> letters = Arrays.asList("a", "b", "c", "d", "e");
        String result21 = letters
                .stream()
                .reduce("", (partialString, element) -> partialString + element);
        System.out.println(result21);  //abcde


        String result22 = letters
                .stream()
                .reduce("", String::concat);
        System.out.println(result22);  //ancde

        //3、複雜物件歸約
        //計算所有的員工的年齡總和。
        Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
        Employee e2 = new Employee(2,13,"F","Martina","Hengis");
        Employee e3 = new Employee(3,43,"M","Ricky","Martin");
        Employee e4 = new Employee(4,26,"M","Jon","Lowman");
        Employee e5 = new Employee(5,19,"F","Cristine","Maria");
        Employee e6 = new Employee(6,15,"M","David","Feezor");
        Employee e7 = new Employee(7,68,"F","Melissa","Roy");
        Employee e8 = new Employee(8,79,"M","Alex","Gussin");
        Employee e9 = new Employee(9,15,"F","Neetu","Singh");
        Employee e10 = new Employee(10,45,"M","Naveen","Jain");


        List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);

        //先用map將Stream流中的元素由Employee型別處理為Integer型別(age)。
        //然後對Stream流中的Integer型別進行歸約
        Integer total = employees.stream().map(Employee::getAge).reduce(0,Integer::sum);
        System.out.println(total); //346

        //4、Combiner合併器的使用
        /*
        * 除了使用map函式實現型別轉換後的集合歸約,我們還可以用Combiner合併器來實現,這裡第一次使用到了Combiner合併器。
        * 因為Stream流中的元素是Employee,累加器的返回值是Integer,所以二者的型別不匹配。
        * 這種情況下可以使用Combiner合併器對累加器的結果進行二次歸約,相當於做了型別轉換
        * */
        Integer total3 = employees.stream()
                .reduce(0,(totalAge,emp) -> totalAge + emp.getAge(),Integer::sum); //注意這裡reduce方法有三個引數
        System.out.println(total3); //346

        //5、*並行流資料歸約(使用合併器)
        //在進行並行流計算的時候,可能會將集合元素分成多個組計算。為了更快的將分組計算結果累加,可以使用合併器。
        Integer total2 = employees
                .parallelStream()
                .map(Employee::getAge)
                .reduce(0,Integer::sum,Integer::sum);  //注意這裡reduce方法有三個引數即合併器

        System.out.println(total2); //346
    }
}

結果

21
21
abcde
abcde
346
346
346

Process finished with exit code 0

11.StreamAPI終端操作

12.java8如何排序Map

13.Stream流逐行檔案處理

14.java8-forEach(持續釋出中)