<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 13 差異對比:集合在 Java 7 和 8 有何不同和改進 ## 引導語 Java 8 在 Java 7 的基礎上,做了一些改進和優化,但我們在平時工作中,或者直接升級到 Java 8 的過程中,我們好像無需做任何兼容邏輯,那么 Java 8 底層是如何處理的呢,在改進的同時,是如何優雅兼容 Java 老版本,讓使用者無需感知,接下來我們通過對比 Java 7 和 8 的差異,來展示 Java 8 是如何優雅升級的。 #### 1 通用區別 #### 1.1 所有集合都新增了forEach 方法 List、Set、Map 在 Java 8 版本中都增加了 forEach 的方法,方法的入參是 Consumer,Consumer 是一個函數式接口,可以簡單理解成允許一個入參,但沒有返回值的函數式接口,我們以 ArrayList 的 forEach 的源碼為例,來看下方法是如何實現的 : ``` @Override public void forEach(Consumer<? super E> action) { // 判斷非空 Objects.requireNonNull(action); // modCount的原始值被拷貝 final int expectedModCount = modCount; final E[] elementData = (E[]) this.elementData; final int size = this.size; // 每次循環都會判斷數組有沒有被修改,一旦被修改,停止循環 for (int i=0; modCount == expectedModCount && i < size; i++) { // 執行循環內容,action 代表我們要干的事情 action.accept(elementData[i]); } // 數組如果被修改了,拋異常 if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } ``` 從這段源碼中,很容易產生兩個問題: 1、action.accept 到底是個啥? action.accept 就是你在 for 循環中要干的事情,你可以進行任何事情,比如我們打印一句話,如下: ``` public void testForEach(){ List<Integer> list = new ArrayList<Integer>(){{ add(1); add(3); add(2); add(4); }}; // value 是每次循環的入參,就是 list 中的每個元素 list.forEach( value->log.info("當前值為:{}",value)); } ``` 輸出為: ``` 當前值為:1 當前值為:3 當前值為:2 當前值為:4 log.info(“當前值為:{}”,value) 就是我們要干的事情,就是 action。 ``` 2、forEach 方法上打了 @Override 注解,說明該方法是被繼承實現的,該方法是被定義在 Iterable 接口上的,Java 7 和 8 的 ArrayList 都實現了該接口,但我們在 Java 7 的 ArrayList 并沒有發現有實現該方法,編譯器也木有報錯,這個主要是因為 Iterable 接口的 forEach 方法被加上了 default 關鍵字,這個關鍵字只會出現在接口類中,被該關鍵字修飾的方法無需強制要求子類繼承,但需要自己實現默認實現,我們看下源碼: ![](https://img.kancloud.cn/bd/aa/bdaae9f1732a0afcbd6236779045c8cb_1190x363.jpg) 不僅僅是 forEach 這一個方法是這么干的,List、Set、Map 接口中很多新增的方法都是這么干的,通過 default 關鍵字,可以讓 Java 7 的集合子類無需實現 Java 8 中新增的方法。 如果想在接口中新增一個方法,但又不想子類強制實現該方法時,可以給該方法加上 default 關鍵字,這個在實際工作中,也經常使用到,算是重構的小技巧吧。 #### 1.2 List 區別 #### 1.2.1 ArrayList 1. ArrayList 無參初始化時,Java 7 是直接初始化 10 的大小,Java 8 去掉了這個邏輯,初始化時是空數組,在第一次 add 時才開始按照 10 進行擴容,下圖是源碼的差異對比圖: ![](https://img.kancloud.cn/0c/c2/0cc20b1a1d87393e087b477bb2249adc_2006x254.jpg) 2. List 其它方面 java7 和 8 并沒有改動。 #### 1.3 Map 區別 #### 1.3.1 HashMap 1. 和 ArrayList 一樣,Java 8 中 HashMap 在無參構造器中,丟棄了 Java 7 中直接把數組初始化 16 的做法,而是采用在第一次新增的時候,才開始擴容數組大小; 2. hash 算法計算公式不同,Java 8 的 hash 算法更加簡單,代碼更加簡潔; 3. Java 8 的 HashMap 增加了紅黑樹的數據結構,這個是 Java 7 中沒有的,Java 7 只有數組 + 鏈表的結構,Java 8 中提出了數組 + 鏈表 + 紅黑樹的結構,一般 key 是 Java 的 API 時,比如說 String 這些 hashcode 實現很好的 API,很少出現鏈表轉化成紅黑樹的情況,因為 String這些API 的 hash 算法夠好了,只有當 key 是我們自定義的類,而且我們覆寫的 hashcode 算法非常糟糕時,才會真正使用到紅黑樹,提高我們的檢索速度。 ***** 也是因為 Java 8 新增了紅黑樹,所以幾乎所有操作數組的方法的實現,都發生了變動,比如說 put、remove 等操作,可以說 Java 8 的 HashMap 幾乎重寫了一遍,所以 Java 7 的很多問題都被 Java 8 解決了,比如擴容時極小概率死鎖,丟失數據等等。 ***** 4. 新增了一些好用的方法,比如 getOrDefault,我們看下源碼,非常簡單: ``` // 如果 key 對應的值不存在,返回期望的默認值 defaultValue public V getOrDefault(Object key, V defaultValue) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? defaultValue : e.value; } ``` 還有 putIfAbsent(K key, V value) 方法,意思是,如果 map 中存在 key 了,那么 value 就不會覆蓋,如果不存在 key ,新增成功。 還有 compute 方法,意思是允許我們把 key 和 value 的值進行計算后,再 put 到 map 中,為防止 key 值不存在造成未知錯誤,map 還提供了 computeIfPresent 方法,表示只有在 key 存在的時候,才執行計算,demo 如下: ``` @Test public void compute(){ HashMap<Integer,Integer> map = Maps.newHashMap(); map.put(10,10); log.info("compute 之前值為:{}",map.get(10)); map.compute(10,(key,value) -> key * value); log.info("compute 之后值為:{}",map.get(10)); // 還原測試值 map.put(10,10); // 如果為 11 的 key 不存在的話,需要注意 value 為空的情況,下面這行代碼就會報空指針 // map.compute(11,(key,value) -> key * value); // 為了防止 key 不存在時導致的未知異常,我們一般有兩種辦法 // 1:自己判斷空指針 map.compute(11,(key,value) -> null == value ? null : key * value); // 2:computeIfPresent 方法里面判斷 map.computeIfPresent(11,(key,value) -> key * value); log.info("computeIfPresent 之后值為:{}",map.get(11)); } ``` 結果是: ``` compute 之前值為:10 compute 之后值為:100 computeIfPresent 之后值為:null(這個結果中,可以看出,使用 computeIfPresent 避免了空指針) ``` 上述 Java 8 新增的幾種方法非常好用,在實際工作中,可以大大減少我們的代碼量,computeIfPresent 的源碼就不貼了,有興趣可以去 github 上面查看,主要的實現原理如下: * 找到 key 對應的老值,會分別從數組、鏈表、紅黑樹中找; * 根據 key 和老值進行計算,得到新值; * 用新值替換掉老值,可能是普通替換、鏈表替換或紅黑樹替換。 #### 1.3.2 LinkedHashMap 由于 Java 8 的底層數據有變動,導致 HashMap 操作數據的方法幾乎重寫,也使 LinkedHashMap 的實現名稱上有所差異,原理上都相同,我們看下面的圖,左邊是 Java 7,右邊是 Java 8。 ![](https://img.kancloud.cn/36/31/36313c3d03f48a02d5036ba53c84b298_2468x1320.jpg) 從圖中,我們發現 LinkedHashMap 的方法名有所修改,底層的實現邏輯其實都差不多的。 #### 1.4 其他區別 #### 1.4.1 Arrays 提供了很多 parallel 開頭的方法。 Java 8 的 Arrays 提供了一些 parallel 開頭的方法,這些方法支持并行的計算,在數據量大的時候,會充分利用 CPU ,提高計算效率,比如說 parallelSort 方法,方法底層有判斷,只有數據量大于 8192 時,才會真正走并行的實現,在實際的實驗中,并行計算的確能夠快速的提高計算速度。 #### 1.5 面試題 1. Java 8 在 List、Map 接口上新增了很多方法,為什么 Java 7 中這些接口的實現者不需要強制實現這些方法呢? 答:主要是因為這些新增的方法被 default 關鍵字修飾了,default 一旦修飾接口上的方法,我們需要在接口的方法中寫默認實現,并且子類無需強制實現這些方法,所以 Java 7 接口的實現者無需感知。 2. Java 8 中有新增很多實用的方法,你在平時工作中有使用過么? 答:有的,比如說 getOrDefault、putIfAbsent、computeIfPresent 方法等等,具體使用細節參考上文。 3. 說說 computeIfPresent 方法的使用姿勢? 答:computeIfPresent 是可以對 key 和 value 進行計算后,把計算的結果重新賦值給 key,并且如果 key 不存在時,不會報空指針,會返回 null 值。 4. Java 8 集合新增了 forEach 方法,和普通的 for 循環有啥不同? 答:新增的 forEach 方法的入參是函數式的接口,比如說 Consumer 和 BiConsumer,這樣子做的好處就是封裝了 for 循環的代碼,讓使用者只需關注實現每次循環的業務邏輯,簡化了重復的 for 循環代碼,使代碼更加簡潔,普通的 for 循環,每次都需要寫重復的 for 循環代碼,forEach 把這種重復的計算邏輯吃掉了,使用起來更加方便。 5. HashMap 8 和 7 有啥區別? 答:HashMap 8 和 7 的差別太大了,新增了紅黑樹,修改了底層數據邏輯,修改了 hash 算法,幾乎所有底層數組變動的方法都重寫了一遍,可以說 Java 8 的 HashMap 幾乎重新了一遍。 ### 總結 總體來說,List 方面是小改動,HashMap 幾乎重寫了一套,所有的集合都新增了函數式的方法,比如說 forEach,也新增了很多好用的函數,比如說 getOrDefault,這些函數可以大大減少我們的代碼量,讓我們把關注點聚焦在業務邏輯的實現上,這其實是一種思想,把繁瑣重復的計算邏輯抽取出來,從計算邏輯中擴展出業務邏輯的口子,讓使用者只專心關注業務邏輯的實現即可。 想要了解更多差異,也可直接前往 JDK 8 新特性查看,地址為:http://openjdk.java.net/projects/jdk8/features#103。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看