<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          用Stream來優(yōu)化老代碼,瞬間干凈優(yōu)雅了!

          共 4453字,需瀏覽 9分鐘

           ·

          2021-12-26 00:12

          1、延時任務實現(xiàn)方案

          2、服務端如何防止重復支付

          3、五年工作經(jīng)驗總結 16 條的代碼規(guī)范

          4、突發(fā)!Apache Log4j2 報核彈級漏洞

          5、都在建議你不要直接使用 @Async 注解,為什么?

          Java8 的新特性主要是 Lambda 表達式和流,當流和 Lambda 表達式結合起來一起使用時,因為流申明式處理數(shù)據(jù)集合的特點,可以讓代碼變得簡潔易讀。

          01 流如何簡化代碼

          如果有一個需求,需要對數(shù)據(jù)庫查詢到的菜肴進行一個處理:
          • 篩選出卡路里小于 400 的菜肴
          • 對篩選出的菜肴進行一個排序
          • 獲取排序后菜肴的名字
          Dish.java(菜肴)
          public?class?Dish?{
          ????private?String?name;
          ????private?boolean?vegetarian;
          ????private?int?calories;
          ????private?Type?type;
          ????//?getter?and?setter
          }
          Java8 以前的實現(xiàn)方式
          private?List?beforeJava7(List?dishList)?{
          ????List?lowCaloricDishes?=?new?ArrayList<>();

          ????//1.篩選出卡路里小于400的菜肴
          ????for?(Dish?dish?:?dishList)?{
          ????????if?(dish.getCalories()?400
          )?{
          ????????????lowCaloricDishes.add(dish);
          ????????}
          ????}

          ????//2.對篩選出的菜肴進行排序
          ????Collections.sort(lowCaloricDishes,?new?Comparator()?{
          ????????@Override
          ????????public?int?compare(Dish?o1,?Dish?o2)?{
          ????????????return?Integer.compare(o1.getCalories(),?o2.getCalories());
          ????????}
          ????});

          ????//3.獲取排序后菜肴的名字
          ????List?lowCaloricDishesName?=?new?ArrayList<>();
          ????for?(Dish?d?:?lowCaloricDishes)?{
          ????????lowCaloricDishesName.add(d.getName());
          ????}

          ????return?lowCaloricDishesName;
          }
          Java8 之后的實現(xiàn)方式
          private?List?afterJava8(List?dishList)?{
          ????return?dishList.stream()
          ????????????.filter(d?->?d.getCalories()?400
          )?//篩選出卡路里小于400的菜肴
          ????????????.sorted(comparing(Dish::getCalories))?//根據(jù)卡路里進行排序
          ????????????.map(Dish::getName)?//提取菜肴名稱
          ????????????.collect(Collectors.toList());?//轉(zhuǎn)換為List
          }
          不拖泥帶水,一氣呵成,原來需要寫 24 代碼實現(xiàn)的功能現(xiàn)在只需 5 行就可以完成了
          高高興興寫完需求這時候又有新需求了,新需求如下:
          對數(shù)據(jù)庫查詢到的菜肴根據(jù)菜肴種類進行分類,返回一個?Map?的結果
          這要是放在 JDK8 之前肯定會頭皮發(fā)麻
          Java8 以前的實現(xiàn)方式
          private?static?Map>?beforeJDK8(List?dishList)?{
          ????Map>?result?=?new?HashMap<>();

          ????for?(Dish?dish?:?dishList)?{
          ????????//不存在則初始化
          ????????if?(result.get(dish.getType())==null)?{
          ????????????List?dishes?=?new?ArrayList<>();
          ????????????dishes.add(dish);
          ????????????result.put(dish.getType(),?dishes);
          ????????}?else?{
          ????????????//存在則追加
          ????????????result.get(dish.getType()).add(dish);
          ????????}
          ????}

          ????return?result;
          }
          還好 JDK8 有 Stream,再也不用擔心復雜集合處理需求
          Java8 以后的實現(xiàn)方式
          private?static?Map>?afterJDK8(List?dishList)?{
          ????return?dishList.stream().collect(groupingBy(Dish::getType));
          }
          又是一行代碼解決了需求,忍不住大喊 Stream API 牛批 看到流的強大功能了吧,接下來將詳細介紹流

          02 什么是流

          流是從支持數(shù)據(jù)處理操作的源生成的元素序列,源可以是數(shù)組、文件、集合、函數(shù)。流不是集合元素,它不是數(shù)據(jù)結構并不保存數(shù)據(jù),它的主要目的在于計算。

          03 如何生成流

          生成流的方式主要有五種:
          1.通過集合生成,應用中最常用的一種
          List?integerList?=?Arrays.asList(1,?2,?3,?4,?5);
          Stream?stream?=?integerList.stream();
          通過集合的 stream 方法生成流
          2.通過數(shù)組生成
          int[]?intArr?=?new?int[]{1,?2,?3,?4,?5};
          IntStream?stream?=?Arrays.stream(intArr);
          通過?Arrays.stream?方法生成流,并且該方法生成的流是數(shù)值流【即?IntStream?】而不是 Stream。補充一點使用數(shù)值流可以避免計算過程中拆箱裝箱,提高性能。
          Stream API 提供了mapToIntmapToDouble、mapToLong三種方式將對象流【即 Stream】轉(zhuǎn)換成對應的數(shù)值流,同時提供了?boxed?方法將數(shù)值流轉(zhuǎn)換為對象流
          3.通過值生成
          Stream?stream?=?Stream.of(1,?2,?3,?4,?5);
          通過 Stream 的?of?方法生成流,通過 Stream 的?empty?方法可以生成一個空流
          4.通過文件生成
          Stream?lines?=?Files.lines(Paths.get("data.txt"),?Charset.defaultCharset())
          通過 Files.line 方法得到一個流,并且得到的每個流是給定文件中的一行
          5.通過函數(shù)生成 提供了 iterate 和 generate 兩個靜態(tài)方法從函數(shù)中生成流
          iterator
          Stream?stream?=?Stream.iterate(0,?n?->?n?+?2).limit(5);
          iterate 方法接受兩個參數(shù),第一個為初始化值,第二個為進行的函數(shù)操作,因為 iterator 生成的流為無限流,通過 limit 方法對流進行了截斷,只生成 5 個偶數(shù)
          generator
          Stream?stream?=?Stream.generate(Math::random).limit(5);
          generate 方法接受一個參數(shù),方法參數(shù)類型為 Supplier,由它為流提供值。generate 生成的流也是無限流,因此通過 limit 對流進行了截斷

          04 流的操作類型

          流的操作類型主要分為兩種: 中間操作、終端操作。

          中間操作

          一個流可以后面跟隨零個或多個中間操作。其目的主要是打開流,做出某種程度的數(shù)據(jù)映射/過濾,然后返回一個新的流,交給下一個操作使用。這類操作都是惰性化的,僅僅調(diào)用到這類方法,并沒有真正開始流的遍歷,真正的遍歷需等到終端操作時,常見的中間操作有下面即將介紹的?filter、map?等

          終端操作

          一個流有且只能有一個終端操作,當這個操作執(zhí)行后,流就被關閉了,無法再被操作,因此一個流只能被遍歷一次,若想在遍歷需要通過源數(shù)據(jù)在生成流。終端操作的執(zhí)行,才會真正開始流的遍歷。如下面即將介紹的?count、collect?等

          05 流使用

          流的使用將分為終端操作和中間操作進行介紹

          中間操作

          filter 篩選
          List?integerList?=?Arrays.asList(1,?1,?2,?3,?4,?5);
          Stream?stream?=?integerList.stream().filter(i?->?i?>?3);
          通過使用?filter?方法進行條件篩選,filter?的方法參數(shù)為一個條件
          distinct 去除重復元素
          List?integerList?=?Arrays.asList(1,?1,?2,?3,?4,?5);
          Stream?stream?=?integerList.stream().distinct();
          通過?distinct?方法快速去除重復的元素
          limit 返回指定流個數(shù)
          List?integerList?=?Arrays.asList(1,?1,?2,?3,?4,?5);
          Stream?stream?=?integerList.stream().limit(3);
          通過?limit?方法指定返回流的個數(shù),limit 的參數(shù)值必須 >=0,否則將會拋出異常
          skip 跳過流中的元素
          List?integerList?=?Arrays.asList(1,?1,?2,?3,?4,?5);
          Stream?stream?=?integerList.stream().skip(2);
          通過?skip?方法跳過流中的元素,上述例子跳過前兩個元素,所以打印結果為 2,3,4,5,skip?的參數(shù)值必須 >=0,否則將會拋出異常
          map 流映射
          所謂流映射就是將接受的元素映射成另外一個元素
          List?stringList?=?Arrays.asList("Java?8",?"Lambdas",?"In",?"Action");
          Stream?stream?=?stringList.stream().map(String::length);
          復制代碼
          通過?map?方法可以完成映射,該例子完成中?String -> Integer?的映射,之前上面的例子通過?map?方法完成了?Dish->String?的映射
          flatMap 流轉(zhuǎn)換
          將一個流中的每個值都轉(zhuǎn)換為另一個流
          List?wordList?=?Arrays.asList("Hello",?"World");
          List?strList?=?wordList.stream()
          ????????.map(w?->?w.split("?"))
          ????????.flatMap(Arrays::stream)
          ????????.distinct()
          ????????.collect(Collectors.toList());
          map(w -> w.split(" "))?的返回值為?Stream,我們想獲取?Stream,可以通過?flatMap?方法完成?Stream ->Stream?的轉(zhuǎn)換
          元素匹配
          提供了三種匹配方式:
          1.allMatch?匹配所有
          List?integerList?=?Arrays.asList(1,?2,?3,?4,?5);
          if?(integerList.stream().allMatch(i?->?i?>?3))?{
          ????System.out.println("值都大于3");
          }
          2.anyMatch?匹配其中一個
          List?integerList?=?Arrays.asList(1,?2,?3,?4,?5);
          if?(integerList.stream().anyMatch(i?->?i?>?3))?{
          ????System.out.println("存在大于3的值");
          }
          等同于
          for?(Integer?i?:?integerList)?{
          ????if?(i?>?3)?{
          ????????System.out.println("存在大于3的值");
          ????????break;
          ????}
          }
          3.noneMatch?全部不匹配
          List?integerList?=?Arrays.asList(1,?2,?3,?4,?5);
          if?(integerList.stream().noneMatch(i?->?i?>?3))?{
          ????System.out.println("值都小于3");
          }
          通過 noneMatch 方法實現(xiàn)

          終端操作

          統(tǒng)計流中元素個數(shù)
          通過?count
          List?integerList?=?Arrays.asList(1,?2,?3,?4,?5);
          Long?result?=?integerList.stream().count();
          通過使用 count 方法統(tǒng)計出流中元素個數(shù)
          通過 counting
          List?integerList?=?Arrays.asList(1,?2,?3,?4,?5);
          Long?result?=?integerList.stream().collect(counting());
          最后一種統(tǒng)計元素個數(shù)的方法在與 collect 聯(lián)合使用的時候特別有用
          查找
          提供了兩種查找方式
          1、findFirst?查找第一個
          //查找到第一個大于?3?的元素并打印
          List?integerList?=?Arrays.asList(1,?2,?3,?4,?5);
          Optional?result?=?integerList.stream().filter(i?->?i?>?3).findFirst();
          2、findAny?隨機查找一個
          List?integerList?=?Arrays.asList(1,?2,?3,?4,?5);
          Optional?result?=?integerList.stream().filter(i?->?i?>?3).findAny();
          通過?findAny?方法查找到其中一個大于三的元素并打印,因為內(nèi)部進行優(yōu)化的原因,當找到第一個滿足大于三的元素時就結束,該方法結果和?findFirst?方法結果一樣。提供?findAny?方法是為了更好的利用并行流,findFirst?方法在并行上限制更多
          reduce 將流中的元素組合起來
          假設我們對一個集合中的值進行求和
          JDK8 之前:
          int?sum?=?0;
          for?(int?i?:?integerList)?{
          sum?+=?i;
          }
          JDK8 之后通過?reduce?進行處理
          int?sum?=?integerList.stream().reduce(0,?(a,?b)?->?(a?+?b));
          一行就可以完成,還可以使用方法引用簡寫成:
          int?sum?=?integerList.stream().reduce(0,?Integer::sum);
          reduce?接受兩個參數(shù),一個初始值這里是 0,一個?BinaryOperator accumulator?來將兩個元素結合起來產(chǎn)生一個新值,
          另外, reduce 方法還有一個沒有初始化值的重載方法
          獲取流中最小最大值
          通過 min/max 獲取最小最大值
          Optional?min?=?menu.stream().map(Dish::getCalories).min(Integer::compareTo);
          Optional?max?=?menu.stream().map(Dish::getCalories).max(Integer::compareTo);
          也可以寫成:
          OptionalInt?min?=?menu.stream().mapToInt(Dish::getCalories).min();
          OptionalInt?max?=?menu.stream().mapToInt(Dish::getCalories).max();
          min?獲取流中最小值,max?獲取流中最大值,方法參數(shù)為?Comparator comparator
          通過 minBy/maxBy 獲取最小最大值
          Optional?min?=?menu.stream().map(Dish::getCalories).collect(minBy(Integer::compareTo));
          Optional?max?=?menu.stream().map(Dish::getCalories).collect(maxBy(Integer::compareTo));
          minBy?獲取流中最小值,maxBy?獲取流中最大值,方法參數(shù)為?Comparator comparator
          通過?reduce?獲取最小最大值
          Optional?min?=?menu.stream().map(Dish::getCalories).reduce(Integer::min);
          Optional?max?=?menu.stream().map(Dish::getCalories).reduce(Integer::max);
          復制代碼

          07 總結

          這篇文章主要介紹了 Stream API 的相關使用,從文中所列舉的例子可以看出:通過使用 Stream API 可以簡化代碼,同時還提高了代碼可讀性。
          所以,趕緊在項目里用起來吧!

          最近熱文閱讀:

          1、延時任務實現(xiàn)方案
          2、服務端如何防止重復支付
          3、五年工作經(jīng)驗總結 16 條的代碼規(guī)范
          4、為什么我勸你放棄了Restful API?
          5、Java8 Stream:2萬字20個實例,玩轉(zhuǎn)集合的篩選、歸約、分組、聚合
          6、公司規(guī)定所有接口都用 POST請求,這是為什么?
          7、為什么阿里強制 boolean 類型變量不能使用 is 開頭?
          8、面試官:InnoDB中一棵B+樹可以存放多少行數(shù)據(jù)?
          9、MyBatis批量插入幾千條數(shù)據(jù),請慎用foreach
          10、有了 for (;;) ,為什么還需要while (true) ?到底哪個更快?
          關注公眾號,你想要的Java都在這里

          瀏覽 20
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  深夜福利视频久久久久 | 成人网站免费在线观看 | 色狂c熟妇中国日本 | 91久精品| 国产一级色 |