<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【進階篇】使用 Stream 流對比兩個集合的常用操作分享

      前言

      在之前的開發中,遇到了這樣的需求:記錄某個更新操作之前的數據作為日志內容,之后可以供管理員在頁面上查看該日志。

      思路:

      1. 更新接口拿入參與現在數據庫該條數據逐一對比,將不同的部分取出;
      2. 在更新操作前取出現在數據庫的該條數據,更新操作后再取出同一條數據,比較兩者的異同。

      經過短暫對比后,我選擇方案2,理由如下:

      • 前端入參未經過后端真實性校驗,即萬一進來的不是同一條數據呢?這樣是不可靠的。
      • 后端先拿參數去數據庫找,如果有這條數據,那么拿出來做對比可以保證更新的是同一條數據。

      要點:

      1. 從數據庫里拿出來的一條數據其實是個實體類對象,那是否可以兩個對象逐一比較屬性值是否相等呢?這個不現實,因為引用類型的對象在內存中的地址肯定不同,所以對象 .equals() 的結果永遠是 false;
      2. 既然對象不能直接比較,那么就將其先轉換為一個集合后再進行 Stream 操作;
      3. 這里需要比較的兩個集合的元素屬性名相同,但是值不一定相同;

      一、集合的比較

      具體情況可以分為:1、是否需要得到一個新的流?2、是否只需要一個簡單 boolean 結果?

      我開發需求是要得到具體哪些數據不一樣,所以選擇返回一個新的流,只是得到一個 boolean 來判斷是否相同是不夠的。

      1.1需要得到一個新的流

      • 如果是得到一個新的流,那么推薦使用.filter() + .collect()

            @Test
            public void testFilter(){
                //第一個數組
                List<ListData> list1 = new ArrayList<>();
                list1.add(new ListData("測測名字11",11,"email@11"));
                list1.add(new ListData("測測名字22",22,"email@22"));
                list1.add(new ListData("測測名字33",33,"email@33"));
                log.info("第一個數組為:{}", list1);
                //第二個數組
                List<ListData> list2 = new ArrayList<>();
                list2.add(new ListData("測測名字111",111,"email@11"));
                list2.add(new ListData("測測名字22",22,"email@22"));
                list2.add(new ListData("測測名字33",33,"email@33"));
                log.info("第二個數組為:{}", list2);
                //返回一個新的結果數組
                List<ListData> resultList = list1.stream()
                    //最外層的filter里是條件,這個條件需要返回一個boolean:符合條件返回true,不符合條件返回false
                    .filter(p1 -> list2.stream()
                            //這個filter也是條件:判斷兩個數組里名字和年齡是否都相等,符合條件返回true,不符合條件返回false
                            .filter(p2 -> p2.getName().equals(p1.getName()) && p2.getAge().equals(p1.getAge()))
                            //如有內容則返回流中的第一條記錄,其它情況都返回空
                            .findFirst().orElse(null)
                            //這個是最外層的filter的斷言
                            == null)
                    //將上一步流處理的的結果,收集成一個新的集合
                    .collect(Collectors.toList());
                log.info("經過 Stream 流處理后輸出的結果數組為: {}", resultList);
            }
        

        結合.filter() + noneMatch() 其實也與上面的語句效果相同:

               List<ListData> resultList = list1.stream()
                       .filter(p1 -> list2.stream()
                                //這個 noneMatch 也是條件:判斷兩個數組里名字和年齡是否都相等,符合條件返回true,不符合條件返回false
                                .noneMatch(p2 -> p2.getName().equals(p1.getName()) && p2.getAge().equals(p1.getAge())))
                        .collect(Collectors.toList());
               log.info("經過 Stream 流處理后輸出的結果數組為: {}", resultList);
        

        結合 filter() + contains() 方法( 其中 contains() 方法的使用詳見 1.2 小節的注意事項),與以上的效果也一樣:

              List<ListData> resultList = list1.stream().filter(p1 -> !list2.contains(p1)).collect(Collectors.toList());
              log.info("經過 Stream 流處理后輸出的結果數組為: {}", resultList);
        

        下面是以上代碼的運行結果如圖 1 所示:

      圖1

      1.2只需要一個簡單 boolean 結果

      • 如果只需要一個簡單的 boolean 結果,那么推薦使用.anyMatch() 或者 allMatch()

                //返回一個boolean結果
                boolean flag = list1.stream()
                        //只要流中任意一個元素符合條件則返回true,否則返回false
                        .anyMatch(p1 -> list2.stream()
                                //如果流中全部元素都符合條件,就返回true,否則返回false;當流為空時總是返回true
                                .allMatch(p2 -> p2.getName().equals(p1.getName()) && p2.getAge().equals(p1.getAge())));
                log.info("經過 Stream 流對比是否相等: {}", flag);
        

        下面是以上代碼的運行結果如圖 2 所示:

        圖2
      • 除了 Stream 流之外,還可以使用 JDK 自帶的.contains() 相關方法來判斷

        //List 集合接口自帶的方法 
        boolean isEqual = list1.containsAll(list2) && list2.containsAll(list1);
        
        //與上述方法效果一致
        boolean isEqual = list1.stream().anyMatch(p1 -> list2.contains(p1));
        //下面的是上述語句的 lambda 表達式寫法
        //boolean isEqual = list1.stream().anyMatch(list2::contains);
        

        注意事項:.contains() 相關方法底層是迭代器 Iterator 以及 .equals() 方法,需要為 List 集合包含的泛型 中重寫.equals() 方法才能使用,舉例如下所示:

        @Data
        @AllArgsConstructor
        @NoArgsConstructor
        public class ListData {
            private String name;
            private Integer age;
            private String email;
            @Override
            public boolean equals(Object o) {
                if (this == o) return true;
                if (o == null || getClass() != o.getClass()) return false;
                ListData listData = (ListData) o;
                return Objects.equals(name, listData.name) && Objects.equals(age, listData.age) && Objects.equals(email, listData.email);
            }
        }
        

        下面是以上代碼的運行結果如圖 3 所示:

        圖3
      • 理論上可以用 for 循環或者迭代器來做,效果與使用 .containsAll() 方法差不多,但是自己手寫的話可能會比較復雜,數據量稍大些的話效率較低,一般不考慮采用,這里我就不演示了。


      二、簡單集合的對比

      上述的集合都是泛型為自定義引用類型的集合,下面分享一些簡單集合,如整形、字符串類型集合的 Stream 流對比操作。

      2.1整型元素集合

              List<Integer> list1 = Arrays.asList(1, 6,);
              List<Integer> list2 = Arrays.asList(3, 2, 1);
              //Java 本身提供的 Integer 類已經實現了 Comparable 接口,可以直接.sort() 比較
              boolean isEqual = list1.stream().sorted().collect(Collectors.toList())
                  .equals(list2.stream().sorted().collect(Collectors.toList()));
              log.info("是否相等:{}", isEqual);
      

      2.2字符串元素集合

              // 先排序然后轉成 String 逗號分隔,joining()拼接
              List<String> list3 = Arrays.asList("語文","數學","英語");
              List<String> list4 = Arrays.asList("數學","英語","語文");
              //Java 本身提供的 String 類也已經實現了 Comparable 接口
              boolean flag = list3.stream().sorted().collect(Collectors.toList())
                  .equals(list4.stream().sorted().collect(Collectors.toList()));
              log.info("是否相等:{}", flag);
      

      下面是簡單集合比較的運行結果,如圖 4 所示:

      圖4

      2.3其它比較

      不知道大家有沒有發現,上述簡單類型的類可以直接比較,而自己寫的類就不能,會報”cannot be cast to java.lang.Comparable“。

      舉個例子,對于自定義的引用類型 ListData , Java 不知道應該怎樣為 ListData 的對象排序,是應該按名字排序? 還是按年齡來排序?

      注意:.sort() 方法底層實現需要依賴 Comparator 接口,那么這個引用類型 ListData 類要自己手動去實現 Comparator() 接口并重寫 compare() 方法才能這樣做比較。

              List<ListData> list1 = new ArrayList<>();
              list1.add(new ListData("泛型為引用類型", 666, "abc"));
              List<ListData> list2 = new ArrayList<>();
              list2.add(new ListData("泛型為引用類型", 888, "def"));
              //這里想要收集成為集合進行比較,需要先根據特定的元素排序(年齡),然后再按順序比較
              boolean flag = list1.stream().sorted(Comparator.comparing(ListData::getAge)).collect(Collectors.toList())
                      .equals(list2.stream().sorted(Comparator.comparing(ListData::getAge)).collect(Collectors.toList()));
              log.info("是否相等: {}", flag);
      

      三、Stream 基礎回顧

      Stream API 是 Java 8 中最為重要的更新之一,是處理集合的關鍵抽象概念,也是每個 Java 后端開發人員都必須無條件掌握的內容。

      Stream 和 Collection 集合的主要區別:Collection 是內存數據結構,重在數據的存儲;而 Stream 是集合的操作計算,重在一系列的流式操作。

      3.1基本概念

      • Stream 不會自己存儲元素,會返回一個持有結果的新的流;
      • Stream 操作是延遲執行的,即一旦執行終止操作,就執行中間操作鏈,并產生結果;
      • Stream 一旦執行了終止操作,那么就不能再執行中間操作或者其它終止操作。

      3.2 Stream 操作的三個步驟

      3.2.1創建 Stream

      一個數據源(如:集合、數組)來獲取一個流,具體有 3 種方式來創建:

      • 通過集合直接創建(最常用)

        //Java8 中的 Collection 接口被擴展,提供了兩個獲取流的方法:
        //返回一個順序流
        default Stream<E> stream(){}
        //返回一個并行流
        default Stream<E> parallelStream{}
        
      • Arrays 也可以獲取數組流

        //返回一個流
        public static <T> Stream<T> stream(T[] array){}
        
      • 調用 Stream 類靜態方法 of() 來創建流

        public static<T> Stream<T> of(T... values){}
        
      3.2.2中間操作

      每次處理都會返回一個持有結果的新 Stream,即中間操作的方法返回值仍然是 Stream 類型的對象。因此中間操作可以是鏈式的,可對數據源的數據進行 n 次處理,但是在終止操作前,并不會真正執行;

      中間操作可謂是最重要也最常使用的操作,具體分為3種:篩選與切片、映射、排序,如以下表格所示:

      • 篩選與切片

        方法 描 述
        Stream filter(Predicate<? super T> predicate); 篩選,接收 Predicate 的條件,從流中排除某些元素,返回一個符合該條件的流
        Stream limit(long maxSize); 截斷,使其元素的數量不超過給定數量
        Stream skip(long n); 跳過,返回一個扔掉了前 n 個元素的流,若流中元素不足 n 個則返回一個空流,可與 limit() 形成互補
        Stream distinct(); 去重,利用流所生成元素的 hashCode() 和 equals() 去除流中的重復元素
      • 映射

        這里只介紹常見的映射方法,flatMap() 的系列方法并不常用。

        方法 描述
        Stream map(Function<? super T, ? extends R> mapper); 接收一個函數作為參數,該函數會被應用到每個元素上,并將其映射成一個新的元素。
        LongStream mapToLong(ToLongFunction<? super T> mapper); 接收一個函數作為參數,該函數會被應用到每個元素上,產生一個新的 Long 類型的Stream 流。
      • 排序

        方法 描述
        Stream sorted(); 產生一個新流,其中按自然順序(如Integer)排序
        Stream sorted(Comparator<? super T> comparator); 產生一個新流,其中按比較器指定的順序排序
      3.2.3終止操作

      終止操作的方法返回值類型不再是 Stream,而可以是任何不為流的值,如List、Integer 甚至是 void ,因此一旦執行終止操作就會結束整個 Stream操作且不能再次使用。終止操作也很常見,下面就不做具體的分類,都寫在一起了,按需使用即可:

      方法 描述
      boolean anyMatch(Predicate<? super T> predicate); 檢查是否所有元素都符合條件,符合就返回 true,不符合則返回 false
      boolean allMatch(Predicate<? super T> predicate); 檢查是否至少有一個元素符合條件,有則返回 true,無則返回false
      boolean noneMatch(Predicate<? super T> predicate); 檢查是否所有元素都不匹配條件,都不符合則返回 true,其它情況返回false
      Optional findFirst(); 返回流中第一個元素并放置到 Optional 容器中
      Optional findAny(); 返回流中任意一個元素并放置到 Optional 容器中
      long count(); 返回流中元素的總個數
      Optional max(Comparator<? super T> comparator); 經比較器按順序比較后,返回流中最大值
      Optional min(Comparator<? super T> comparator); 經比較器按順序比較后,返回流中最小值
      void forEach(Consumer<? super T> action); 內部迭代,如果要對集合迭代可以直接使用.foreach(),不必經過 Stream
      <R, A> R collect(Collector<? super T, A, R> collector); 將流轉換為其他形式,如:將 Stream 中元素收集成.toList()、.toSet() 等

      這里有個特殊的方法,.groupingBy() 不屬于 Stream 而是屬于 Collectors:

      方法 返回類型 描述
      .stream().collect(Collectors.groupingBy()); public static <T, K> Collector<T, ?, Map<K, List>> 根據流中的某屬性值對流進行分組,屬性為 K,結果為指定的泛型,如 List

      四、文章小結

      文章到這里就結束了,關于 Stream 流 API 是日常開發中經常會遇到的,熟練運用可以提高我們的開發效率,讓我們寫出簡潔易懂的代碼,我們作為后端開發必須重視起來。總有人說它的調試 debug 是個缺點,不妨試試”Trace Current Stream Chain“按鈕,可以追蹤當前流中的鏈式變化。

      那么今天的分享到這里就結束了,如有不足和錯誤,還請大家指正。或者你有其它想說的,也歡迎大家在評論區交流!

      參考文檔:

      posted @ 2024-05-27 08:13  CodeBlogMan  閱讀(1839)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 亚洲av成人网人人蜜臀| 日韩福利视频导航| 国产成人无码aa精品一区| 国产一区二区不卡91| av中文字幕在线二区| 成人网站免费观看| 久久综合给合久久狠狠狠88| 热re99久久精品国产99热| 国产在线线精品宅男网址| 最新国产精品中文字幕| 老湿机69福利区无码| 国产亚洲一区二区三区成人| 日韩精品国产中文字幕| 黑人巨大无码中文字幕无码| 亚洲熟妇自偷自拍另亚洲| 中文字幕人妻精品在线| 免费人成视频网站在线18| 骚虎视频在线观看| 国产精品剧情亚洲二区| 中文字幕午夜福利片午夜福利片97| 国产麻豆精品一区二区三区v视界| 亚日韩精品一区二区三区| 闽侯县| h无码精品动漫在线观看| 九九热在线视频中文字幕| 精品视频在线观看免费观看| 香蕉亚洲欧洲在线一区| 精品人妻一区二区| gogo无码大胆啪啪艺术| 舟曲县| 内射干少妇亚洲69XXX| 国产又黄又硬又粗| 亚洲另类激情专区小说图片| 99RE6在线观看国产精品| 无码av最新无码av专区| 亚洲国产成人午夜在线一区 | 99久久国产综合精品色| 国产综合视频一区二区三区| 高清一区二区三区不卡视频| 亚洲成人四虎在线播放| 亚洲人成网站免费播放|