<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)雅了!

          共 4504字,需瀏覽 10分鐘

           ·

          2021-12-30 07:59

          往期熱門文章:

          1、真正的緩存之王,Google Guava 只是弟弟

          2、自定義注解妙用,一行代碼搞定用戶操作日志記錄,你學(xué)會了嗎?

          3、離開互聯(lián)網(wǎng)上岸1年后,我后悔了!重回大廠內(nèi)卷

          4、推薦一個開源的任務(wù)管理工具

          5我司Spring Boot 項目打包 + Shell 腳本部署詳細(xì)總結(jié),太有用了!


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

          01 流如何簡化代碼

          如果有一個需求,需要對數(shù)據(jù)庫查詢到的菜肴進(jìn)行一個處理:
          • 篩選出卡路里小于 400 的菜肴
          • 對篩選出的菜肴進(jìn)行一個排序
          • 獲取排序后菜肴的名字
          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.對篩選出的菜肴進(jìn)行排序
          ????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ù)卡路里進(jìn)行排序
          ????????????.map(Dish::getName)?//提取菜肴名稱
          ????????????.collect(Collectors.toList());?//轉(zhuǎn)換為List
          }
          不拖泥帶水,一氣呵成,原來需要寫 24 代碼實現(xiàn)的功能現(xiàn)在只需 5 行就可以完成了
          高高興興寫完需求這時候又有新需求了,新需求如下:
          對數(shù)據(jù)庫查詢到的菜肴根據(jù)菜肴種類進(jìn)行分類,返回一個?Map?的結(jié)果
          這要是放在 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,再也不用擔(dān)心復(fù)雜集合處理需求
          Java8 以后的實現(xiàn)方式
          private?static?Map>?afterJDK8(List?dishList)?{
          ????return?dishList.stream().collect(groupingBy(Dish::getType));
          }
          又是一行代碼解決了需求,忍不住大喊 Stream API 牛批 看到流的強(qiáng)大功能了吧,接下來將詳細(xì)介紹流

          02 什么是流

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

          03 如何生成流

          生成流的方式主要有五種:
          1.通過集合生成,應(yīng)用中最常用的一種
          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 提供了mapToIntmapToDoublemapToLong三種方式將對象流【即 Stream】轉(zhuǎn)換成對應(yīng)的數(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ù),第一個為初始化值,第二個為進(jìn)行的函數(shù)操作,因為 iterator 生成的流為無限流,通過 limit 方法對流進(jìn)行了截斷,只生成 5 個偶數(shù)
          generator
          Stream?stream?=?Stream.generate(Math::random).limit(5);
          generate 方法接受一個參數(shù),方法參數(shù)類型為 Supplier,由它為流提供值。generate 生成的流也是無限流,因此通過 limit 對流進(jìn)行了截斷

          04 流的操作類型

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

          中間操作

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

          終端操作

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

          05 流使用

          流的使用將分為終端操作和中間操作進(jìn)行介紹

          中間操作

          filter 篩選
          List?integerList?=?Arrays.asList(1,?1,?2,?3,?4,?5);
          Stream?stream?=?integerList.stream().filter(i?->?i?>?3);
          通過使用?filter?方法進(jìn)行條件篩選,filter?的方法參數(shù)為一個條件
          distinct 去除重復(fù)元素
          List?integerList?=?Arrays.asList(1,?1,?2,?3,?4,?5);
          Stream?stream?=?integerList.stream().distinct();
          通過?distinct?方法快速去除重復(fù)的元素
          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?方法跳過流中的元素,上述例子跳過前兩個元素,所以打印結(jié)果為 2,3,4,5,skip?的參數(shù)值必須 >=0,否則將會拋出異常
          map 流映射
          所謂流映射就是將接受的元素映射成另外一個元素
          List?stringList?=?Arrays.asList("Java?8",?"Lambdas",?"In",?"Action");
          Stream?stream?=?stringList.stream().map(String::length);
          復(fù)制代碼
          通過?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?隨機(jī)查找一個
          List?integerList?=?Arrays.asList(1,?2,?3,?4,?5);
          Optional?result?=?integerList.stream().filter(i?->?i?>?3).findAny();
          通過?findAny?方法查找到其中一個大于三的元素并打印,因為內(nèi)部進(jìn)行優(yōu)化的原因,當(dāng)找到第一個滿足大于三的元素時就結(jié)束,該方法結(jié)果和?findFirst?方法結(jié)果一樣。提供?findAny?方法是為了更好的利用并行流,findFirst?方法在并行上限制更多
          reduce 將流中的元素組合起來
          假設(shè)我們對一個集合中的值進(jìn)行求和
          JDK8 之前:
          int?sum?=?0;
          for?(int?i?:?integerList)?{
          sum?+=?i;
          }
          JDK8 之后通過?reduce?進(jìn)行處理
          int?sum?=?integerList.stream().reduce(0,?(a,?b)?->?(a?+?b));
          一行就可以完成,還可以使用方法引用簡寫成:
          int?sum?=?integerList.stream().reduce(0,?Integer::sum);
          reduce?接受兩個參數(shù),一個初始值這里是 0,一個?BinaryOperator accumulator?來將兩個元素結(jié)合起來產(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);
          復(fù)制代碼

          07 總結(jié)

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

          往期熱門文章:

          1、歷史文章分類導(dǎo)讀列表!精選優(yōu)秀博文都在這里了!》
          2、程序員裸辭全職接單一個月的感觸
          3Java8 Stream:2萬字20個實例,玩轉(zhuǎn)集合的篩選、歸約、分組、聚合
          4、字節(jié)終面:兩個文件的公共URL怎么找?
          5、留在一線,逃離一線?我從上海舉家回成都的生活經(jīng)歷告訴你
          6、公司規(guī)定所有接口都用 POST請求,這是為什么?
          7、我被這個瀏覽了 746000 次的問題驚住了!
          8、騰訊三面:40億個QQ號碼如何去重?
          9、自從用完Gradle后,有點嫌棄Maven了!速度賊快!
          10、一個員工的離職成本有多恐怖!

          瀏覽 31
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  青青操大香蕉 | 中文字幕+乱码+中文乱码视频在线观看 | 中文天堂视频在线 | 一区二区三区免费无码 | 国产av最新福利 国产jk在线观看 |