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

          共 13834字,需瀏覽 28分鐘

           ·

          2022-01-09 18:41

          ????關(guān)注后回復(fù) “進(jìn)群” ,拉你進(jìn)程序員交流群????

          來源丨Java技術(shù)江湖

                    


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

          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<String> beforeJava7(List<Dish> dishList) {
              List<Dish> lowCaloricDishes = new ArrayList<>();

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

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

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

              return lowCaloricDishesName;
          }
          Java8 之后的實現(xiàn)方式
             
          private List<String> afterJava8(List<Dish> 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<Type, List> 的結(jié)果
          這要是放在 JDK8 之前肯定會頭皮發(fā)麻
          Java8 以前的實現(xiàn)方式
             
          private static Map<Type, List<Dish>> beforeJDK8(List<Dish> dishList) {
              Map<Type, List<Dish>> result = new HashMap<>();

              for (Dish dish : dishList) {
                  //不存在則初始化
                  if (result.get(dish.getType())==null) {
                      List<Dish> 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<Type, List<Dish>> afterJDK8(List<Dish> 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<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
          Stream<Integer> stream = integerList.stream();
          通過集合的 stream 方法生成流
          2.通過數(shù)組生成
             
          int[] intArr = new int[]{12345};
          IntStream stream = Arrays.stream(intArr);
          通過 Arrays.stream 方法生成流,并且該方法生成的流是數(shù)值流【即 IntStream 】而不是 Stream。補(bǔ)充一點(diǎn)使用數(shù)值流可以避免計算過程中拆箱裝箱,提高性能。
          Stream API 提供了mapToInt、mapToDouble、mapToLong三種方式將對象流【即 Stream】轉(zhuǎn)換成對應(yīng)的數(shù)值流,同時提供了 boxed 方法將數(shù)值流轉(zhuǎn)換為對象流
          3.通過值生成
             
          Stream<Integer> stream = Stream.of(12345);
          通過 Stream 的 of 方法生成流,通過 Stream 的 empty 方法可以生成一個空流
          4.通過文件生成
             
          Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())
          通過 Files.line 方法得到一個流,并且得到的每個流是給定文件中的一行
          5.通過函數(shù)生成 提供了 iterate 和 generate 兩個靜態(tài)方法從函數(shù)中生成流
          iterator
             
          Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);
          iterate 方法接受兩個參數(shù),第一個為初始化值,第二個為進(jìn)行的函數(shù)操作,因為 iterator 生成的流為無限流,通過 limit 方法對流進(jìn)行了截斷,只生成 5 個偶數(shù)
          generator
             
          Stream<Double> stream = Stream.generate(Math::random).limit(5);
          generate 方法接受一個參數(shù),方法參數(shù)類型為 Supplier,由它為流提供值。generate 生成的流也是無限流,因此通過 limit 對流進(jìn)行了截斷

          04 流的操作類型

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

          中間操作

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

          終端操作

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

          05 流使用

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

          中間操作

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

          終端操作

          統(tǒng)計流中元素個數(shù)
          通過 count
             
          List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
          Long result = integerList.stream().count();
          通過使用 count 方法統(tǒng)計出流中元素個數(shù)
          通過 counting
             
          List<Integer> integerList = Arrays.asList(12345);
          Long result = integerList.stream().collect(counting());
          最后一種統(tǒng)計元素個數(shù)的方法在與 collect 聯(lián)合使用的時候特別有用
          查找
          提供了兩種查找方式
          1、findFirst 查找第一個
             
          //查找到第一個大于 3 的元素并打印
          List<Integer> integerList = Arrays.asList(12345);
          Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst();
          2、findAny 隨機(jī)查找一個
             
          List<Integer> integerList = Arrays.asList(12345);
          Optional<Integer> 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<Integer> min = menu.stream().map(Dish::getCalories).min(Integer::compareTo);
          Optional<Integer> 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<? super T> comparator
          通過 minBy/maxBy 獲取最小最大值
             
          Optional<Integer> min = menu.stream().map(Dish::getCalories).collect(minBy(Integer::compareTo));
          Optional<Integer> max = menu.stream().map(Dish::getCalories).collect(maxBy(Integer::compareTo));
          minBy 獲取流中最小值,maxBy 獲取流中最大值,方法參數(shù)為 Comparator<? super T> comparator
          通過 reduce 獲取最小最大值
             
          Optional<Integer> min = menu.stream().map(Dish::getCalories).reduce(Integer::min);
          Optional<Integer> max = menu.stream().map(Dish::getCalories).reduce(Integer::max);
          復(fù)制代碼

          07 總結(jié)

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

          -End-

          最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

          點(diǎn)擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

          在看點(diǎn)這里好文分享給更多人↓↓

          瀏覽 26
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(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>
                  啪啪啪网址| 人人爱人人操黄色大片 | 欧美靠逼免费网站 | 一级a毛片免费看 | 自拍偷拍2025 |