JavaSE - 16JDK8新特性2


JavaSE - 16JDK8新特性2

(5)強大的Stream API

(5.1)StreamAPI說明

  • Java8中兩大最為重要的改變,一個是Lambda表達式;另一個就是Stream API
  • StreamAPI(java.util.stream)把真正的函數式編程風格引入到Java中,讓程序員可以寫出高效、干凈、簡潔的代碼。
  • StreamAPI是Java8中處理集合的關鍵抽象概念,可以指定希望對集合進行的操作,也可以執行非常復雜的查找、過濾和映射數據等操作。
    • 使用StreamAPI對集合數據進行操作,類似於使用SQL執行的數據庫查詢。也可以使用StreamAPI來並行執行操作。
  • Collection 和 Stream集合的區別: Collection是一種靜態的內存數據結構,而Stream是有關計算的。
    • 前者是主要面向內存, 存儲在內存中,后者主要是面向CPU, 通過CPU實現計算。

(5.2)什么是Stream

  • Stream 是數據渠道,用於操作數據源(集合、數組等)所生成的元素序列。 集合講的是數據,Stream講的是計算。
  • Stream 自己不會存儲元素。
  • Stream 不會改變源對象。相反, 他們會返回一個持有結果的新Stream。
  • Stream 操作是延遲執行的。 這意味着他們會等到需要結果的時候才執行。
  • Stream 操作步驟:
    • 創建Stream: 一個數據源(如:集合、數組),獲取一個流
    • 中間操作:    一個中間操作鏈,對數據源的數據進行處理
    • 終止操作:    一旦執行終止操作,就執行中間操作鏈,並產生結果通知。之后,不會再被使用。

(5.3)Stream 創建

(5.3.1)創建Stream方式一: 通過集合

  • Java 8中的Collection 接口被擴展,提供了兩個獲取流的方法:
    • default Stream<E> stream(): 返回一個順序流
    • default Stream<E> parallelStream(): 返回一個並行流
        List<Employee> employees = EmployeeData.getEmployees();
        // default Stream<E> stream(): 返回一個順序流
        Stream<Employee> stream = employees.stream();
        // parallelStream(): 返回一個並行流
        Stream<Employee>  parallelStream = employees.parallelStream();

(5.3.2)創建Stream方式二: 通過數組

        // 調用Arrays類的 static <T> Stream<T> stream(T[] array): 返回一個流
        int[] arr = new int[]{1,2,3,45,6};
        IntStream stream2 = Arrays.stream(arr);

        Employee e1 = new Employee(1,"Tom");
        Employee e2 = new Employee(2,"Jack");
        Employee[] emp = new Employee[]{e1,e2};
        Stream<Employee> stream3 = Arrays.stream(emp);

(5.3.3)創建Stream方式三: 通過Stream的of()

        Stream<Integer> stream4 = Stream.of(1,2,3,4,5,6);
        Stream<Employee> stream5 = Stream.of(e1,e2);

(5.3.4)創建Stream方式四: 創建無限流

        // 迭代:public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
        Stream.iterate(0, t -> t+2).forEach(System.out::println);
        // 遍歷前10個偶數
        Stream.iterate(0, t -> t+2).limit(10).forEach(System.out::println);

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

(5.4)Stream 中間操作

  • 多個中間操作可以連接起來形成一個流水線, 除非流水線上觸發終止操作,否則中間操作不會執行任何的處理。
  • 在終止操作時一次性全部處理,稱為"惰性求值"。
(5.4.1)篩選與切片
  • filter(Predicate p):  接收Lambda, 從流中排除某些元素
  • distinct():           篩選,通過流所生成元素的 hashCode() 和 equals() 去除重復元素
  • limit(long maxSize):  截斷流, 使其元素不超過給定數量
  • skip(long n):         跳過元素,返回一個去掉了前n個元素的流。若流中元素不足n個,則返回一個空流。與limit(n)互補。

        List<Employee> employees = EmployeeData.getEmployees();
        Stream<Employee> stream = employees.stream();
        stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);

        stream.limit(3).forEach(System.out::println);

        stream.skip(3).forEach(System.out::println);

        stream.distinct().forEach(System.out::println);

(5.4.2)映射 

  • map(Function f):                 接收一個函數作為參數,該參數會被應用到每個元素上,並將其映射成一個新的元素。
  • mapToDouble(ToDoubleFunction f): 接收一個函數作為參數,該參數會被應用到每個元素上,產生一個新的DoubleStream。
  • mapToInt(ToIntFunction f):       接收一個函數作為參數,該參數會被應用到每個元素上,產生一個新的IntStream。
  • mapToLong(ToLongFunction f):     接收一個函數作為參數,該參數會被應用到每個元素上,產生一個新的LongStream。
  • flatMap(Function f):             接收一個函數作為參數,將流中的每個值都轉換成另一個流,然后把所有的流連接成一個流。
        List<String> list = Arrays.asList("aa","vv","cc","dd");
        list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);

        List<Employee> employees = EmployeeData.getEmployees();
        Stream<Employee> stream = employees.stream();
        Stream<String> nameStream = stream.map(Employee::getName);
        nameStream.filter(name -> name.length() > 3).forEach(System.out::println);

        Stream<Stream<Character>> streamStream = list.stream().map(StreamTest::fromStringToStream);
        streamStream.forEach(s -> {
            s.forEach(System.out::println);
        });

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

 

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

(5.4.3)排序

        // sorted():自然排序
        List<Integer> list = Arrays.asList(12,43,65,34,87,1,-9,19);
        list.stream().sorted().forEach(System.out::println);
        // sorted(Comparator com): 定制排序
        List<Employee> employees = EmployeeData.getEmployees();
        employees.stream().sorted((e1,e2) ->{return Integer.compare(e1.getAge(), e2.getAge());}).forEach(System.out::println);


        employees.stream().sorted((e1,e2) ->{
            int ageValue = Integer.compare(e1.getAge(), e2.getAge());
            if(ageValue != 0){
                return ageValue;
            }else{
                return Double.compare(e1.getSalary(), e2.getSalary());
            }
        }).forEach(System.out::println);

 

 

(5.5)Stream 終止操作

  • 終端操作會從流的流水線生成結果。其結果可以是 任何不是流的值,如List、Integer,甚至是 void。
  • 流進行了終止操作后,不能再次使用。

(5.5.1)匹配與查找

  • allMatch(Predicate p):  檢查是否匹配所有元素
  • anyMatch(Predicate p):  檢查是否至少匹配一個元素
  • noneMatch(Predicate p): 檢查是否沒有匹配所有元素
  • findFirst():            返回第一個元素
  • findAny():              返回當前流中的任意元素
        //  是否所有的員工年齡都大於18
        boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
        System.out.println(allMatch);
        // 是否有任意一個員工的工資大於10000
        boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
        System.out.println(anyMatch);
        // 是否所有員工都不姓"雷",
        boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
        System.out.println(noneMatch);
        // 返回第一個元素
        Optional<Employee> firstEmp = employees.stream().findFirst();
        System.out.println(firstEmp);
        // 返回任意元素
        Optional<Employee> anyEmp = employees.parallelStream().findAny();
        System.out.println(anyEmp);

 

        // 返回最大值
        Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
        Optional<Double> maxSalary = salaryStream.max(Double::compare);
        System.out.println(maxSalary.get());  //Optional[9865.38]   9865.38
        // 返回最小值
        Optional<Employee> minSalaryEmp = employees.stream().min((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary()));
        System.out.println(minSalaryEmp); //Optional[Employee{id=1008, name='扎克伯格', age=35, salary=2500.38}]
        // forEach(Consumer c): 內部迭代
        employees.stream().forEach(System.out::println);

(5.5.2)歸約

  • reduce(T iden, BinaryOperator b):  可以將流中元素反復結合起來,得到一個值。返回T
  • reduce(BinaryOperator b):          可以將流中元素反復結合起來,得到一個值。返回 Optional<T>
  • 備注: map 和reduce 的連接通常稱為 map-reduce模式,因Google用它進行網絡搜索而出名。
 
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        int sum  = list.stream().reduce(0,Integer::sum);  
        System.out.println(sum); // 55

        List<Employee> employees = EmployeeData.getEmployees();
        Optional<Double> salarySum = employees.stream().map(e -> e.getSalary()).reduce(Double::sum);
        System.out.println(salarySum); // Optional[48675.03999999999]
        Optional<Double> salarySum2 = employees.stream().map(Employee::getSalary).reduce((d1,d2) -> d1+d2);
        System.out.println(salarySum2); // Optional[48675.03999999999]

 (5.5.2)收集

  • collect(Collector c):  將流轉換為其他形式。接收一個Collector接口的實現,用於給Stream中元素做匯總的方法。

Collector 接口中方法的實現決定了如何對流執行收集的操作(如收集到List、Set、Map)。

另外,Collectors實用類提供了很多靜態方法,可以創建常見收集器實例。

方法 返回類型 作用
toList List<T> 把流中元素收集到List
toSet Set<T> 把流中元素收集到Set
toCollection Collection<T> 把流中元素收集到創建的集合
counting Long 計算流中元素的個數
summingInt Integer 對流中元素的整數屬性求和
avaragingInt Double 計算流中元素Integer屬性的平均值
summarizingInt IntSummaryStatistics 收集流中Integer屬性的統計值。如平均值、最大值、最小值、總數量

 

 

 

 

 

 

 

 

 

 

 

        List<Employee> employees = EmployeeData.getEmployees();
        List<Employee> empList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
        empList.forEach(System.out::println);

        Set<Employee> empSet= employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
        empSet.forEach(System.out::println);

 

 

 

 

 

        Collection<Employee> emps = employees.stream().collect(Collectors.toCollection(ArrayList::new));
        System.out.println(emps);
        long count = employees.stream().collect(Collectors.counting());
        System.out.println(count); // 8
        double totalSalary = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(totalSalary); //48675.04
        double avgSalary = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(avgSalary); //6084.38
        DoubleSummaryStatistics dss = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(dss.getMax() + "--" + dss.getMin() + "--" + dss.getAverage()); //9865.38--2500.38--6084.38

 

 

 

 

 

(6)Optional類

 

  • 為了解決空指針異常,Google公司的Guava項目引入了Optional類,Guava通過使用檢查空值的方法來防止代碼污染。
    • 受Google Guava啟發,Optional類已經成為Java 8類庫的一部分。
  • Optional<T> 類(java.util.Optional)是一個容器類,可以保存類型T的值,代表這個值存在。或者僅僅保存null,表示這個值不存在。
    • 原來用null表示一個值不存在,現在Optional可以更好的表達這個概念,並且可以避免空指針異常。
  • Optional類的JavaDoc描述如下: 這是一個可以為null的容器對象。
    • 如果值存在則isPresent()方法會返回true, 調用get()方法會返回該對象。

(6.1)Optional類方法

 

  • 創建Optional類對象的方法:
    • Optional.of(T t):         創建一個 Optional實例,t 必須非空
    • Optional.empty():         創建一個空的Optional實例
    • Optional.ofNullable(T t): t 可以為null
  • 判斷Optional 容器中是否包含對象:
    • boolean isPresent():      判斷是否包含對象
    • void ifPresent(Consumer<? super T> consumer): 如果有值,執行Consumer接口的實現代碼,並且該值會作為參數傳給它。
  • 獲取Optional容器的對象
    • T get():                  如果調用對象包含值,返回該值,否則拋異常
    • T orElse(T other):        如果有值則將其返回,否則返回指定的other對象
    • T orElseGet(Supplier<? extends T> other):  如果有值將其返回,否則返回由Supplier接口實現提供的對象。
    • T orElseThrow(Supplier<? extends X> exceptionSupplier): 如果有值將其返回,否則拋出由Supplier接口實現提供的異常。

 

        Girl girl = new Girl();
        girl = null;
//        Optional<Girl> optionalGirl = Optional.of(girl); // java.lang.NullPointerException

        Girl girl2 = new Girl();
        girl2 = null;
        Optional<Girl> optionalGirl2 = Optional.ofNullable(girl2);

        Girl girl3 = optionalGirl2.orElse(new Girl("春天",11));
        System.out.println(girl3); 
        //  Girl{name='null', age=0, boy=null}   未添加 girl2 =null時
        //  Girl{name='春天', age=11, boy=null} 

 

未做判斷時: 報空指針異常

 

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

    @Test
    public void test2(){
        Boy boy = new Boy();
        String girlName = getGirlName(boy);  // java.lang.NullPointerException
        System.out.println(girlName);
    }

 

 

 

做判斷: 返回null

 

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

        if(boy != null){
            Girl girl = boy.getGirl();
            if(girl != null){
                return girl.getName();
            }
        }
        return null;
    }

 

 

 

使用Optional 實現

 

    public String getGirlName2(Boy boy){
        Optional<Boy> boyOptional = Optional.ofNullable(boy);
        Boy boy2 = boyOptional.orElse(new Boy(new Girl("地理",23)));
        Girl girl = boy2.getGirl();
        Optional<Girl> girlOptional = Optional.ofNullable(girl);
        Girl girl2 =  girlOptional.orElse(new Girl("古老",12));
        return girl2.getName();
    }
// boy=null時 boy2將Girl進行賦值; boy=new Boy()時 boy非空,到了 girl2時對Girl進行賦值 @Test
public void test3(){ Boy boy = null; boy = new Boy(); String girlName = getGirlName2(boy); System.out.println(girlName); // boy=null: 地理 boy=new Boy():古老 }

 

 

 

 

 

 

        Optional<Object> op1 = Optional.empty();
        if(!op1.isPresent()){
            System.out.println("數據為空");   // 數據為空
        }
        System.out.println(op1);             // Optional.empty
        System.out.println(op1.isPresent()); // false
        System.out.println(op1.get());       // java.util.NoSuchElementException: No value present

 

 

 

 

 

 

 

        String str = "hello";
        str = null;
        // of()要求t非空,如t為空,get()方法報錯
        Optional<String> op2 = Optional.of(str);
        System.out.println(op2.get());  // hello     加入str=null: java.lang.NullPointerException

 

 

 

 

 

 

        String str2 = "beijn";
        str2 = null;
        Optional<String> op3 = Optional.ofNullable(str2);
        String str3 = op3.orElse("shanho");
        System.out.println(str3);             // beijn  加入str2=null: shanho

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM