<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>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ## 14 簡化工作:Guava Lists Maps 實際工作運用和源碼 ## 引導語 在日常工作中,我們經常會使用一些三方的 API 來簡化我們的工作,Guava 就是其中一種,Guava 是 Google 開源的技術框架,使用率高,社區活躍度也很高。 本小節我們從工作中對 Guava 集合的使用入手,然后深入的看下其底層的實現,最后總結其設計思想,感興趣的同學也可以下載源碼學習,GitHub 地址:https://github.com/google/guava,源碼中 guava 的文件夾為其源碼。 ### 1 運用工廠模式進行初始化 在集合類初始化方面,Guava 比 Java 原生的 API 更加好用,還發明了很多新的功能,比如說在 JDK 7 之前,我們新建集合類時,聲明和初始化都必須寫上泛型說明,像這樣:List<泛型> list = new ArrayList<泛型>(); , JDK 7 之后有所改變,我們只需要在聲明處寫上泛型說明,像這樣:List<泛型> list = new ArrayList<>();。 Guava 提供了更加方便的使用姿勢,采用了工廠模式,把集合創建的邏輯交給了工廠,開發者無需關注工廠底層是如何創建的,只需要關心,工廠能產生什么,代碼于是變成了這樣:List<泛型> list = Lists.newArrayList();,Lists 就是 Guava 提供出來的,方便操作 List 的工具類。 這種寫法其實就是一種簡單的工廠模式,只需要定義好工廠的入參和出參,就能對外隱藏其內部的創建邏輯,提供更加方便的使用體驗。 當然除了 Lists,Guava 還提供了很多其他實用工具,如 Maps、Sets,接下來我們分別來看下這些常用工具的使用和原理。 ### 2 Lists #### 2.1 初始化 Lists 最大的功能是能幫助我們進行 List 的初始化,比如我們剛說的 newArrayList 這種: ``` List<String> list = Lists.newArrayList(); public static <E> ArrayList<E> newArrayList() { return new ArrayList<>(); } // 這種底層是幫助我們寫好了泛型,E 代表泛型,表示當前返回的泛型類型和聲明的一致即可,在編譯的時候,會把泛型 E 轉化成我們聲明的 String。 ``` ``` 如果你清楚 List 的大小,我們也可以這樣做: // 可以預估 list 的大小為 20 List<String> list = Lists.newArrayListWithCapacity(20); // 不太肯定 list 大小是多少,但期望是大小是 20 上下。 List<String> list = Lists.newArrayListWithExpectedSize(20); ``` newArrayListWithCapacity(20) 方法內部實現是:new ArrayList<>(20);,而 newArrayListWithExpectedSize 方法內部實現是對 List 大小有一個計算公式的,計算公式為:5L + arraySize + (arraySize / 10) ,arraySize 表示傳進來的值,公式簡化下就是 5 + 11/10 * arraySize,因為這個方法表示期望的大小,所以這里取的約是期望值的十分之十一,比傳進來的值約大十分之一,所以根據 20 最終計算出來的值是 27。 Lists 在初始化的時候,還支持傳迭代器的入參(只適合小數據量的迭代器的入參),源碼如下: ``` public static <E> ArrayList<E> newArrayList(Iterator<? extends E> elements) { ArrayList<E> list = newArrayList(); // addAll 方法底層其實通過迭代器進行 for 循環添加 Iterators.addAll(list, elements); return list; } ``` 從 Lists 對 List 初始化進行包裝的底層源碼來看,底層源碼非常簡單的,但我們還是愿意使用這種方式的包裝,主要是因為這種工廠模式的包裝,使我們的使用姿勢更加優雅,使用起來更加方便。 #### 2.2 分組和反轉排序 除了初始化之外,Lists 還提供了兩個比較實用的功能,分組和反轉排序功能,我們分別來演示一下: ``` // 演示反轉排序 public void testReverse(){ List<String> list = new ArrayList<String>(){{ add("10"); add("20"); add("30"); add("40"); }}; log.info("反轉之前:"+JSON.toJSONString(list)); list = Lists.reverse(list); log.info("反轉之后:"+JSON.toJSONString(list)); } // 打印出來的結果為: 反轉之前:["10","20","30","40"] 反轉之后:["40","30","20","10"] ``` reverse 方法底層實現非常巧妙,底層覆寫了 List 原生的 get(index) 方法,會把傳進來的 index 進行 (size - 1) - index 的計算,使計算得到的索引位置和 index 位置正好相反,這樣當我們 get 時,數組索引位置的 index 已經是相反的位置了,達到了反轉排序的效果,其實底層并沒有進行反轉排序,只是在計算相反的索引位置,通過計算相反的索引位置這樣簡單的設計,得到了反轉排序的效果,很精妙。 在工作中,有時候我們需要把一個大的 list 進行切分,然后再把每份丟給線程池去運行,最后將每份運行的結果匯總,Lists 工具類就提供了一個對 list 進行切分分組的方法,演示 demo 如下: ``` // 分組 public void testPartition(){ List<String> list = new ArrayList<String>(){{ add("10"); add("20"); add("30"); add("40"); }}; log.info("分組之前:"+JSON.toJSONString(list)); List<List<String>> list2 = Lists.partition(list,3); log.info("分組之后:"+JSON.toJSONString(list2)); } 輸出結果為: 分組之前:["10","20","30","40"] 分組之后:[["10","20","30"],["40"]] ``` partition 方法的第二個參數的意思,你想讓分組后的 List 包含幾個元素,這個方法的底層實現其實就是 subList 方法。 有一點需要我們注意的是這兩個方法返回的 List 并不是 ArrayList,是自定義的 List,所以對于 ArrayList 的有些功能可能并不支持,使用的時候最好能看下源碼,看看底層有無支持。 #### 2.3 小結 Lists 上述的方法大大的方便了我們進行開發,簡化了使用姿勢,但其內部實現卻非常簡單巧妙,比如說 reverse 方法可以輸出相反排序的 List,但底層并沒有實現排序,只是計算了索引位置的相反值而已,這點值得我們學習。 ### 3 Maps #### 3.1 初始化 Maps 也是有著各種初始化 Map 的各種方法,原理不說了,和 Lists 類似,我們演示下如何使用: ``` Map<String,String> hashMap = Maps.newHashMap(); Map<String,String> linkedHashMap = Maps.newLinkedHashMap(); // 這里 Map 的初始化大小公式和 HashSet 初始化公式類似,還記得 HashSet 初始化 HashMap 時,經典的計算初始大小的公式么:取最大值(期望的值 / 0.75 + 1,默認值 16),newHashMapWithExpectedSize 方法底層也是這么算的初始化大小的 Map<String,String> withExpectedSizeHashMap = Maps.newHashMapWithExpectedSize(20); ``` #### 3.2 difference Maps 提供了一個特別有趣也很實用的方法:difference,此方法的目的是比較兩個 Map 的差異,入參就是兩個 Map,比較之后能夠返回四種差異: 1. 左邊 Map 獨有 key。 2. 右邊 Map 獨有 key。 3. 左右邊 Map 都有 key,并且 value 相等。 4. 左右邊 Map 都有 key,但是 value 不等。 我們用代碼來演示一下: ``` // ImmutableMap.of 也是 Guava 提供初始化 Map 的方法,入參格式為 k1,v1,k2,v2,k3,v3…… Map<String,String> leftMap = ImmutableMap.of("1","1","2","2","3","3"); Map<String,String> rightMap = ImmutableMap.of("2","2","3","30","4","4"); MapDifference difference = Maps.difference(leftMap, rightMap); log.info("左邊 map 獨有 key:{}",difference.entriesOnlyOnLeft()); log.info("右邊 map 獨有 key:{}",difference.entriesOnlyOnRight()); log.info("左右邊 map 都有 key,并且 value 相等:{}",difference.entriesInCommon()); log.info("左右邊 map 都有 key,但 value 不等:{}",difference.entriesDiffering()); ``` 最后打印結果為: ``` 左邊 map 獨有 key:{1=1} 右邊 map 獨有 key:{4=4} 左右邊 map 都有 key,并且 value 相等:{2=2} 左右邊 map 都有 key,但 value 不等:{3=(3, 30)} ``` 從這個 demo 我們可以看到此方法的強大威力,我們在工作中經常遇到 Map 或者 List 間比較差異的任務,我們就可以直接使用該方法進行對比,List 可以先轉化成 Map。 而且 difference 底層的實現也算是最優的實現了,只需要循環一遍,就可得到上述四種差異結果,源碼解析如下: ``` // 對比兩個 map 的差異 private static <K, V> void doDifference( Map<? extends K, ? extends V> left, Map<? extends K, ? extends V> right, Equivalence<? super V> valueEquivalence, // key 只在左邊 map 出現 Map<K, V> onlyOnLeft, // key 只在右邊 map 出現,調用 doDifference 方法前已經包含了全部右邊的值 Map<K, V> onlyOnRight, // key 在左右 map 中都出現過,并且 value 都相等 Map<K, V> onBoth, // key 在左右 map 中都出現過,但 value 不等 Map<K, MapDifference.ValueDifference<V>> differences) { // 以左邊 map 為基準進行循環 for (Entry<? extends K, ? extends V> entry : left.entrySet()) { K leftKey = entry.getKey(); V leftValue = entry.getValue(); // 右邊 map 包含左邊的 key if (right.containsKey(leftKey)) { // onlyOnRight 已經包含全部右邊的值 所以需要刪除當前 key V rightValue = onlyOnRight.remove(leftKey); // key 相等,并且 value 值也相等 if (valueEquivalence.equivalent(leftValue, rightValue)) { onBoth.put(leftKey, leftValue); // key 相等,但 value 值不等 } else { differences.put(leftKey, ValueDifferenceImpl.create(leftValue, rightValue)); } // 右邊 map 不包含左邊的 key,就是左邊 map 獨有的 key } else { onlyOnLeft.put(leftKey, leftValue); } } } ``` 這是一種比較優秀的,快速比對的算法,可以好好看下上面的源碼,然后把這種算法背下來,或者自己再次實現一次。 Sets 的使用方式和 Lists 和 Maps 很類似,沒有太大的亮點,我們就不說了。 ### 4 總結 這一小節主要都是實戰內容,在實際工作中可以用起來。 在 Guava 對集合的設計中,有兩個大點是非常值得我們學習的: 1. Lists、Maps 的出現給我們提供了更方便的使用姿勢和方法,我們在實際工作中,如果碰到特別繁瑣,或者特別難用的 API,我們也可以進行一些包裝,使更好用,這個是屬于在解決目前的痛點的問題上進行創新,是非常值得提倡的一件事情,往往可以幫助你拿到更好的績效。 2. 如果有人問你,List 或者 Map 高效的差異排序算法,完全可以參考 Maps.difference 的內部實現,該方法只使用了一次循環,就可得到所有的相同或不同結果,這種算法在我們工作中也經常被使用。 了解更多,可以直接前往 Guava 的代碼庫查看:https://github.com/google/guava
                  <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>

                              哎呀哎呀视频在线观看