JDK8特性

2020-10-20 11:00:01

1.Map的改造、方法區的新實現以及介面的新規定

map的新實現

這個其實大家面試經常會遇到,但是其實也比較簡單,記住就ok了
(1)Map的底層由陣列加連結串列的方式改為了陣列加連結串列加紅黑樹的實現方式
(2)對key進行hash,然後再根據陣列長度取餘之後的連結串列的頭插法改為了尾插法
(3)ConcurrentHashMap由原來的分段加Hashtable的實現方式,變為了跟Map一樣的方式,只是資料的執行緒安全採用了cas鎖的機制
連結串列改紅黑樹的條件是連結串列的長度超過8,原因是,連結串列長度過長時,查詢效率會變低,而紅黑樹的本質則是一棵有序的二元樹,而二元樹的遍歷分為三種方式,左中右,中左右,右中左,具體採用的哪種遍歷方式沒關注,但是不管具體採用哪種方式,遍歷不需要經過所有節點,每經過一個節點,就減少了差不多一半的元素的被命中的可能性,查詢的效能會變得很高,而相應的插入的效能就變低了。

方法區的新實現
另外就是方法區的實現由堆的一塊固定的記憶體稱為永久代,轉變為了元空間,且採用的是物理機器的記憶體,這個轉變的可能原因是因為,永久代本身發生垃圾回收的概率極為苛刻,另外永久代其實主要儲存的是我們的程式碼編譯之後載入進記憶體的組合指令和常數池,這一塊兒可能發生變動比較少以及垃圾回收能節省的空間幾乎可以忽略,所以就移出來了吧。
介面的新規定

介面可以有具有實現體的方法了,但必須制定為預設方法,關鍵字為:default
注意事項:

  • (1) 當子類繼承的類和實現的介面的預設方法,有同名方法時,優先呼叫繼承父類別的具體實現
  • (2)當子類實現多介面時,多介面存在同名預設方法時,子類必須實現預設方法,指定呼叫的具體預設方法

2.Lambda函數語言程式設計以及新符號(::)的方法呼叫

lambda表示式其實解決的是個匿名函數的問題,另外還給我們總結了四種型別的介面,不用我們的程式碼中去定義那麼多其實並沒有實際實現的java介面檔案,直接採用預設的這四種型別的介面以及他們的子類即可

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 *  Lambda語法總結
 *     通用寫法: 一句處理程式碼的情況下  () -> code;
 *                 多句話的情況下  () -> {} ;
 *     函數shi介面有四種:
 *        1.消費型介面:Consumer(T t) 無返回值
 *        2.供給型介面:Supplier(T)  無引數,有返回值
 *        3.函數型介面:Function(T,R) 有引數且需要返回值的介面
 *        4.判斷型介面:Predicat(T) 返回True/False
 */
public class LambdaTest {

    public static void main(String[] args){
       /* // 原寫法
        Comparator<Integer> com = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };
        TreeSet<Integer> treeSet = new TreeSet<>(com);

        // lambda寫法
        Comparator<Integer> comparator = (x ,y) -> Integer.compare(x,y);
        TreeSet<Integer> treeSetComparator = new TreeSet<>(comparator);*/

        Consumer<Integer> consumer = x -> System.out.println("consumer:"+x);
        consumer.accept(1);

        Supplier<Integer> supplier =  () -> 1;
        Integer i = supplier.get();
        System.out.println("supplier:"+i);

        Function<Integer,Integer> function = x -> x+1;
        Integer fi = function.apply(1);
        System.out.println("function:"+fi);

        Predicate<Integer> predicate = x -> x > 0;
        boolean pFlag = predicate.test(1);
        System.out.println("predicate:"+pFlag);
    }
}

方法的呼叫:採用新的操作符

::

,這其實是對函數語言程式設計的一種支援,進一步降低lambda的程式碼量

import java.io.PrintStream;
import java.util.Collections;
import java.util.List;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 *   方法的呼叫:採用新的操作符(::),這其實是對函數語言程式設計的一種支援,進一步降低lambda的程式碼量
 *     ::用法又分為以下幾種:
 *       1. 範例物件 :: 實體方法名
 *       2. 類名 :; 靜態方法名
 *       3. 類名 :: 實體方法名
 *      建構函式其實也是:類名 :: new
 *      陣列的構造:類名[] :: new
 *    注意點:
 *        1.方法的引數以及返回值的確定是由你定義接收的函數式介面來進行推斷得到的
 *        2.呼叫過載的方法時,由你定義的函數式介面的引數型別來推斷的,也就是你要呼叫的
 *    方法的參數列必須跟函數式介面定義的參數列一直
 *        3.類名 :: 實體方法這種呼叫方式其實是對於引數中有一個是實體方法的呼叫者時使用的,比如equlas方法
 */
public class MethodCalledTest {

    public static void main(String[] args){

        // 1.範例物件 :: 實體方法名
        Consumer<String> consumer = (x) -> System.out.println(x);

        PrintStream out = System.out;
        Consumer<String> consumer1 = out::println;// 實際使用經常為System.out::println

        // 2. 類名 :; 靜態方法名
        List<Integer> list = Collections.emptyList();
        Supplier<List<Integer>> supplier = Collections::emptyList;
        list = supplier.get();

        // 2. 類名 :; 實體方法名
        BiPredicate<String,String> biPredicate = (x,y) -> x.equals(y);
        biPredicate = String::equals;

        //  構造方法的使用 ClassName :; new
        Supplier<String> supplier1 = String::new;
        Function<String,String> function = String::new;// 呼叫String的有參建構函式

        // 陣列  類名[] :: new
        Function<Integer,String[]> function1 = String[]::new;
        String[] array = function1.apply(10);// Integer 為陣列長度
    }
}

3.Stream 流式操作

Stream Api 通過一個管道對集合和陣列進行一系列的操作

  •  stream() : 序列流,資料處理是一個個進行的,執行緒安全
    
  •  parallelStream() :並行流,資料處理是多執行緒處理的,執行緒不安全,但是巨量資料量情況下,效能好,實現方式,其實就是Fork/Join框架的方式,充分利用多核的cpu
    
  • Stream Api 包含三個步驟:
  • 1.建立流
  • 2.流式操作
  • 3.終止操作
  • 流有三個特點:
  •  1.Stream本身不儲存元素
    
  •  2.Stream不會改變源物件,它的操作都會返回一個持有結果的新的Stream物件
    
  •  3.Stream操作是延遲執行的,它只有等到終止操作時才會去真正的進行計算,稱為「惰性求值」
    

建立流的方式:

 /**
     *   建立流有四種方式
     *      1.通過集合本身提供的stream()或者parallelStream()方法建立
     *      2.通過Arrays的靜態方法Arrays.stream(T t)建立
     *      3.通過Stream類本身的靜態方法 Stream.of("aa","bb","cc")建立
     *      4.無限流有兩種建立方式
     *         1)迭代建立流Stream.iterate(0,x -> x+2),需要一個引數和一個有引數有返回值的lambda表示式
     *         2)生成建立流 Stream.generate(() -> Math.random()),需要一個供給型的lambda的表示式
     */
    public void  createStream(){
        // 1.通過Collection集合的stream()或者parallelStream() 建立流
        List<String>  list = new ArrayList<>();
        Stream<String> stream1 =  list.stream();
        Stream<String> parallelStream = list.parallelStream();

        // 2.通過Arrays的靜態方法stream(),建立流
        String[] strArray = new String[]{};
        Stream<String> stream2 = Arrays.stream(strArray);

        // 3.通過Stream類本身的靜態方法of建立
        Stream<String> stream3 = Stream.of("a","b","c");

        // 4.無限流
        Stream<Integer> stream4 = Stream.iterate(0,x -> x +2 );
        Stream<Double> stream5 = Stream.generate(() -> Math.random());
    }

流水線的操作:

篩選與切片
 /**
     *  1.篩選與切片
     *     (1)filter -  接収一個斷言型別的lambda,過濾掉不滿足的條件的元素
     *     (2)limit(n)  - 截斷流,收集不超過給定數量的元素,收集滿了就不繼續進行了
     *    (3)skip(n) - 跳過元素,返回一個丟掉了前n個元素的流,若元素不足n個返回空流,與limit互補
     *    (4)distinct - 篩選去掉重複元素,篩選通過的是元素的hashCode()和equals()方法
     */
    public void opreateStream1(){
        List<Integer> list = Arrays.asList(1,0,3,2,6,5,4);
        // filter -  接収一個斷言型別的lambda,過濾掉不滿足的條件的元素
        Stream stream = list.stream()
                .filter(x -> {
                    // 可以註釋掉stream.forEach,可以看到這個列印不執行
                    // 這個就是我們說的惰性求值,只有收集結果才執行中間操作
                    System.out.println(x);
                    return x > 0;
                });
        stream.forEach(System.out :: println);

        // limit(n)  - 截斷流,收集不超過給定數量的元素,收集滿了就不繼續進行了
        list.stream()
                .filter(x -> {
                    // 這裡除了上面的還可以看到斷路器的作用,找到了滿足條件的結果,後面的就不執行了
                    System.out.println(x);
                    return x > 0;
                }).limit(1).forEach(System.out :: println);//  終止操作才能看到結果

        // skip(n) - 跳過元素,返回一個丟掉了前n個元素的流,若元素不足n個返回空流,與limit互補
        list.stream()
                .filter(x -> x > 0)
                .skip(2)
                .forEach(System.out :: println);

        // distinct - 篩選去掉重複元素,篩選通過的是元素的hashCode()和equals()方法
        list.stream()
                .filter(x -> x > 0)
                .distinct()
                .forEach(System.out :: println);
    }
對映
 /**
     *  2.對映
     *     (1)map -  接収一個函數型介面的lambda,應用於每個元素,並返回一個新的元素,最終返回一個流
     *     (2)flatMap  - 接收一個函數作為引數,將該函數應用於每個元素上並返回一個流,每個元素產生的流再組合在一起產生一個新流返回
     */
    public void opreateStream2(){
        List<String> list = Arrays.asList(1,0,3,2);
        // map -  接収一個函數型介面的lambda,應用於每個元素,並返回一個新的元素
        List<Integer> list1 = list.stream()
                .map(str -> Integer.valueOf(str)) // 將String轉換成為了Integer
                .collect(Collectors.toList());



        // flatMap  - 接收一個函數作為引數,將該函數應用於每個元素上並返回一個流,每個元素產生的流再組合在一起產生一個新流返回
        List<String> listList = Arrays.asList("abc","bcd");

        List<Stream<Character>> characterListList = listList.stream()
                .map(StreamTest :: getElementChar) // 結果為{{a,b,c},{b,c,d}}
                .collect(Collectors.toList()); // 收集結果為一個流的集合

        List<Character> characterList = listList.stream()
                .flatMap(StreamTest :: getElementChar) // 結果為{a,b,c,b,c,d}
                .collect(Collectors.toList()); // 收集結果為一個char的集合
    }

    public static Stream<Character> getElementChar(String str){
        List<Character> characters = new ArrayList<>();
        for (Character c: str.toCharArray()) {
             characters.add(c);
        }
       return  characters.stream();
    }
排序
 /**
     *  3.排序
     *     (1)sorted -  自然排序,按照每個元素物件內部實現的CompareTo方法排序
     *     (2)sorted(Comparator m)  - 客製化排序,接收一個比較器的匿名函數,按照匿名函數的比較方法排序
     */
    public void opreateStream3(){
		List<Integer> list = Arrays.asList(1,0,3,2);
        // sorted -  自然排序,按照每個元素物件內部實現的CompareTo方法排序
        list.stream()
                .sorted()
                .forEach(System.out::println);
        
        // sorted(Comparator m)  - 客製化排序,接收一個比較器的匿名函數,按照匿名函數的比較方法排序
        list.stream()
                .sorted((x ,y) -> {
                    if(x < y){
                        return y;
                    }else {
                        return x;
                    }
                })
                .forEach(System.out::println);

    }

終止操作:

查詢與匹配
/**
     *   查詢與匹配
     *     (1)allMatch - 接收一個斷言lambda表示式,判斷是否所有元素都滿足這個斷言
     *     (2)anyMatch - 接收一個斷言lambda表示式,判斷是否至少有一個元素滿足這個斷言
     *     (3)noneMatch - 接收一個斷言lambda表示式,判斷是否所有元素都不滿足這個斷言
     *     (4)findFirst - 查詢第一個元素,返回一個Optional,防止空指標
     *     (5)findAny - 查詢任意一個元素,返回一個Optional,防止空指標
     *     (6)count - 返回流中元素的個數
     *     (7)max - 返回流中元素的比較的最大的結果
     *     (8)min - 返回流中元素的比較的最小的結果
     */
    public void finishStream(){
        List<Integer> list = Arrays.asList(1,0,3,2);
        // allMatch - 接收一個斷言lambda表示式,判斷是否所有元素都滿足這個斷言
        boolean flag = list.stream()
                .allMatch(x -> x > 2);
        System.out.println(flag);

        // anyMatch - 接收一個斷言lambda表示式,判斷是否至少有一個元素滿足這個斷言
        boolean flag1 = list.stream()
                .anyMatch(x -> x > 2);
        System.out.println(flag1);

        // noneMatch - 接收一個斷言lambda表示式,判斷是否所有元素都不滿足這個斷言
        boolean flag2 = list.stream()
                .noneMatch(x -> x > 2);
        System.out.println(flag2);

        // findFirst - 查詢第一個元素,返回一個Optional,防止空指標
        Optional<Integer> optional =list.stream()
                .filter(x -> x >2)
               .findFirst();
        System.out.println(optional.get());

        // findAny - 查詢任意一個元素,返回一個Optional,防止空指標
        Optional<Integer> optional1 =list.stream()
                .filter(x -> x >2)
                .findAny();
        System.out.println(optional1.get());

        // count - 返回流中元素的個數
        long count =list.stream()
                .filter(x -> x >2)
                .count();
        System.out.println(count);

        // max - 返回流中元素的比較的最大的結果
        Optional<Integer> optional2 = list.stream()
               .max((x ,y) -> {
                   if(x < y){
                       return y;
                   }else {
                       return x;
                   }
               });
        System.out.println(optional2.get());
    }
規約

map和reduce通常進行連線使用,稱為map-reduce模式,巨量資料的處理,經常使用這種模式

 /**
     *   規約
     *     (1)reduce - reduce(T identity, BinaryOperator<T> accumulator) 第一個引數為起始指定值,且identity做為二元運算
     *     的T引數進行一個表示式的運算的結果作為二元運算的T進行下一次計算,直到流中元素全部處理完
     *     (2)reduce - Optional<T> reduce(BinaryOperator<T> accumulator)無起始值,直接計算結果
     */
    public void finishStream1(){
        List<Integer> list = Arrays.asList(1,0,3,2);

        /**
         * reduce - reduce(T identity, BinaryOperator<T> accumulator) 第一個引數為起始指定值,且identity做為二元運算
         * 的T引數進行一個表示式的運算的結果作為二元運算的T進行下一次計算,直到流中元素全部處理完
         *
         */
        Integer sum = list.stream()
                .reduce(0,(x,y) -> x+y);
        System.out.println(sum);

        /**
         * reduce - Optional<T> reduce(BinaryOperator<T> accumulator)
         * 無起始值,直接計算結果
         */
        Optional<Integer> optional = list.stream()
                .reduce((x,y) -> x+y);
        System.out.println(optional.get());
    }
收集
/**
     *   收集
     *     (1)Collector - 接收Collector介面的匿名函數,進行流的元素的收集
     */
    public void finishStream2(){

        List<Integer> list = Arrays.asList(1,0,3,2,1,2);

        // Collector - Collectors.toList()
        List<Integer> list1 = list.stream()
                .filter(x -> x > 1)
                .collect(Collectors.toList());

        // Collector - Collectors.toSet()
        Set<Integer> set = list.stream()
                .filter(x -> x > 1)
                .collect(Collectors.toSet());

        // Collector - Collectors.toCollection()
        HashSet<Integer> set1 = list.stream()
                .filter(x -> x > 1)
                .collect(Collectors.toCollection(HashSet::new));

        // Collector - Collectors.counting() 元素總個數
        long count = list.stream()
                .filter(x -> x > 1)
                .collect(Collectors.counting());

        // Collector - Collectors.averagingDouble() 元素的平均值
        Double aDouble = list.stream()
                .filter(x -> x > 1)
                .collect(Collectors.averagingDouble(x -> Double.valueOf(x)));

        // Collector - Collectors.maxBy() 最大值
        Optional<Integer> integer = list.stream()
                .filter(x -> x > 1)
                .collect(Collectors.maxBy(Integer::compare));

        // Collector - Collectors.minBy() 最小值
        Optional<Integer> integer1 = list.stream()
                .filter(x -> x > 1)
                .collect(Collectors.minBy(Integer::compare));

        // Collector - Collectors.groupingBy() 分組
        // Collector - Collectors.partitioningBy() 分割區
        // Collector - Collectors.summarizingDouble() 總結  可以獲取平均值,最大最小值等
        // Collector - Collectors.joining() 連線
    }

4.Optipnal防止空指標的新的api類

  • Optional容器類:為了解決空指標異常而存在的
  • Optional容器類並不能避免空指標的問題,但是能快速定位到空指標的位置,並且將常見的一些空判斷放入到容器中,如果判斷層次夠深,則將if,換成.後面是Optional的各種方法這樣程式碼比較整齊
  • 常用方法:
  • (1) Optional.of(T value) value不能為空,為空直接拋空指標異常
    
  • (2) Optional.empty() 建立一個空的容器,呼叫optional.get()拋空指標異常
    
  • (3)Optional.ofNullable(T value) 當value為空的時候,呼叫optional.get()拋空指標異常
    
  • (4)Optional.isPresent() 判斷容器中是否有值
    
  • (5)optional.orElse(T value) 當容器中沒有值的時候,建立一個value的預設值,返回的也是這個預設值,有則返回容器中的值
    
  • (6)optional.orElseGet(Suplier s) 容器中有值返回值,沒值進行函數式介面處理
    
  • (7)optional.map(Function t) 當容器中有值進行函數的處理,返回處理後的值,沒有值,返回一個空的容器Optional.empty()
    
  • (8)optional.flatMap(Function map) 與map類似,但是返回值一定是Optional
    

5.新的日期時間Api

新的時間Api

  • 區別:
  •  原時間Api,執行緒不安全,並且有背於我們常用的時間,故新的時間api全部放在了Java.time包下且採用了統一的國際標準
    
  • 常用的類:
  •  (1)LocalDateTime/LocalDate/LocalTime  三者的方法與範例化一致
    
  •  (2)Instant:時間戳(以Unix元年<1970-01-01 00:00:00>開始到指定時間的毫秒值)
    
  •  (3)duration 計算時間差值的類
    
  •  (4)Period 計算日期差值的類
    
  •  (5)TemporalAdjuster時間校正器 計算一個特殊的時間,TemporalAdjusters 提供很多常用的操作,比如下一個工作日
    
  •  (6)DateTimeFormatter 時間/日期格式化器,DateTimeFormatter.ofPattern("yyyyMMdd")指定特殊格式
    
  •  (7)ZoneDate/ZoneTime/ZoneDateTime 帶時區的一些時間api