Java 8中Stream API的這些奇技淫巧!你都Get到了嗎?

  • 2019 年 10 月 6 日
  • 筆記

Stream簡介

1、Java 8引入了全新的Stream API。這裡的Stream和I/O流不同,它更像具有Iterable的集合類,但行為和集合類又有所不同。

2、stream是對集合對象功能的增強,它專註於對集合對象進行各種非常便利、高效的聚合操作,或者大批量數據操作。

3、只要給出需要對其包含的元素執行什麼操作,比如 「過濾掉長度大於 10 的字元串」、「獲取每個字元串的首字母」等,Stream 會隱式地在內部進行遍歷,做出相應的數據轉換。

為什麼要使用Stream

1、函數式編程帶來的好處尤為明顯。這種程式碼更多地表達了業務邏輯的意圖,而不是它的實現機制。易讀的程式碼也易於維護、更可靠、更不容易出錯。

2、高端

實例數據源

public class Data {      private static List<PersonModel> list = null;        static {          PersonModel wu = new PersonModel("wu qi", 18, "男");          PersonModel zhang = new PersonModel("zhang san", 19, "男");          PersonModel wang = new PersonModel("wang si", 20, "女");          PersonModel zhao = new PersonModel("zhao wu", 20, "男");          PersonModel chen = new PersonModel("chen liu", 21, "男");          list = Arrays.asList(wu, zhang, wang, zhao, chen);      }        public static List<PersonModel> getData() {          return list;      }  } 

Filter

1、遍曆數據並檢查其中的元素時使用。

2、filter接受一個函數作為參數,該函數用Lambda表達式表示。

    /**       * 過濾所有的男性       */      public static void fiterSex(){          List<PersonModel> data = Data.getData();            //old          List<PersonModel> temp=new ArrayList<>();          for (PersonModel person:data) {              if ("男".equals(person.getSex())){                  temp.add(person);              }          }          System.out.println(temp);          //new          List<PersonModel> collect = data                  .stream()                  .filter(person -> "男".equals(person.getSex()))                  .collect(toList());          System.out.println(collect);      }        /**       * 過濾所有的男性 並且小於20歲       */      public static void fiterSexAndAge(){          List<PersonModel> data = Data.getData();            //old          List<PersonModel> temp=new ArrayList<>();          for (PersonModel person:data) {              if ("男".equals(person.getSex())&&person.getAge()<20){                  temp.add(person);              }          }            //new 1          List<PersonModel> collect = data                  .stream()                  .filter(person -> {                      if ("男".equals(person.getSex())&&person.getAge()<20){                          return true;                      }                      return false;                  })                  .collect(toList());          //new 2          List<PersonModel> collect1 = data                  .stream()                  .filter(person -> ("男".equals(person.getSex())&&person.getAge()<20))                  .collect(toList());        }

Map

1、map生成的是個一對一映射,for的作用

2、比較常用

3、而且很簡單

   /**       * 取出所有的用戶名字       */      public static void getUserNameList(){          List<PersonModel> data = Data.getData();            //old          List<String> list=new ArrayList<>();          for (PersonModel persion:data) {              list.add(persion.getName());          }          System.out.println(list);            //new 1          List<String> collect = data.stream().map(person -> person.getName()).collect(toList());          System.out.println(collect);            //new 2          List<String> collect1 = data.stream().map(PersonModel::getName).collect(toList());          System.out.println(collect1);            //new 3          List<String> collect2 = data.stream().map(person -> {              System.out.println(person.getName());              return person.getName();          }).collect(toList());      }

FlatMap

1、顧名思義,跟map差不多,更深層次的操作

2、但還是有區別的

3、map和flat返回值不同

4、Map 每個輸入元素,都按照規則轉換成為另外一個元素。 還有一些場景,是一對多映射關係的,這時需要 flatMap。

5、Map一對一

6、Flatmap一對多

7、map和flatMap的方法聲明是不一樣的

(1) <r> Stream<r> map(Function mapper);

(2) <r> Stream<r> flatMap(Function> mapper);

(3) map和flatMap的區別:我個人認為,flatMap的可以處理更深層次的數據,入參為多個list,結果可以返回為一個list,而map是一對一的,入參是多個list,結果返回必須是多個list。通俗的說,如果入參都是對象,那麼flatMap可以操作對象裡面的對象,而map只能操作第一層。

public static void flatMapString() {          List<PersonModel> data = Data.getData();          //返回類型不一樣          List<String> collect = data.stream()                  .flatMap(person -> Arrays.stream(person.getName().split(" "))).collect(toList());            List<Stream<String>> collect1 = data.stream()                  .map(person -> Arrays.stream(person.getName().split(" "))).collect(toList());            //用map實現          List<String> collect2 = data.stream()                  .map(person -> person.getName().split(" "))                  .flatMap(Arrays::stream).collect(toList());          //另一種方式          List<String> collect3 = data.stream()                  .map(person -> person.getName().split(" "))                  .flatMap(str -> Arrays.asList(str).stream()).collect(toList());      }

Reduce

1、感覺類似遞歸

2、數字(字元串)累加

3、個人沒咋用過

 public static void reduceTest(){          //累加,初始化值是 10          Integer reduce = Stream.of(1, 2, 3, 4)                  .reduce(10, (count, item) ->{              System.out.println("count:"+count);              System.out.println("item:"+item);              return count + item;          } );          System.out.println(reduce);            Integer reduce1 = Stream.of(1, 2, 3, 4)                  .reduce(0, (x, y) -> x + y);          System.out.println(reduce1);            String reduce2 = Stream.of("1", "2", "3")                  .reduce("0", (x, y) -> (x + "," + y));          System.out.println(reduce2);      }

Collect

1、collect在流中生成列表,map,等常用的數據結構

2、toList()

3、toSet()

4、toMap()

5、自定義

/**       * toList       */         * toList       */      public static void toListTest(){          List<PersonModel> data = Data.getData();          List<String> collect = data.stream()                  .map(PersonModel::getName)                  .collect(Collectors.toList());      }        /**       * toSet       */      public static void toSetTest(){          List<PersonModel> data = Data.getData();          Set<String> collect = data.stream()                  .map(PersonModel::getName)                  .collect(Collectors.toSet());      }        /**       * toMap       */      public static void toMapTest(){          List<PersonModel> data = Data.getData();          Map<String, Integer> collect = data.stream()                  .collect(                          Collectors.toMap(PersonModel::getName, PersonModel::getAge)                  );            data.stream()                  .collect(Collectors.toMap(per->per.getName(), value->{              return value+"1";          }));      }        /**       * 指定類型       */      public static void toTreeSetTest(){          List<PersonModel> data = Data.getData();          TreeSet<PersonModel> collect = data.stream()                  .collect(Collectors.toCollection(TreeSet::new));          System.out.println(collect);      }        /**       * 分組       */      public static void toGroupTest(){          List<PersonModel> data = Data.getData();          Map<Boolean, List<PersonModel>> collect = data.stream()                  .collect(Collectors.groupingBy(per -> "男".equals(per.getSex())));          System.out.println(collect);      }        /**       * 分隔       */      public static void toJoiningTest(){          List<PersonModel> data = Data.getData();          String collect = data.stream()                  .map(personModel -> personModel.getName())                  .collect(Collectors.joining(",", "{", "}"));          System.out.println(collect);      }        /**       * 自定義       */      public static void reduce(){          List<String> collect = Stream.of("1", "2", "3").collect(                  Collectors.reducing(new ArrayList<String>(), x -> Arrays.asList(x), (y, z) -> {                      y.addAll(z);                      return y;                  }));          System.out.println(collect);      }

Optional

1、Optional 是為核心類庫新設計的一個數據類型,用來替換 null 值。

2、人們對原有的 null 值有很多抱怨,甚至連發明這一概念的Tony Hoare也是如此,他曾說這是自己的一個「價值連城的錯誤」

3、用處很廣,不光在lambda中,哪都能用

4、Optional.of(T),T為非空,否則初始化報錯

5、Optional.ofNullable(T),T為任意,可以為空

6、isPresent(),相當於 !=null

7、ifPresent(T), T可以是一段lambda表達式 ,或者其他程式碼,非空則執行

public static void main(String[] args) {              PersonModel personModel=new PersonModel();            //對象為空則打出 -          Optional<Object> o = Optional.of(personModel);          System.out.println(o.isPresent()?o.get():"-");            //名稱為空則打出 -          Optional<String> name = Optional.ofNullable(personModel.getName());          System.out.println(name.isPresent()?name.get():"-");            //如果不為空,則打出xxx          Optional.ofNullable("test").ifPresent(na->{              System.out.println(na+"ifPresent");          });            //如果空,則返回指定字元串          System.out.println(Optional.ofNullable(null).orElse("-"));          System.out.println(Optional.ofNullable("1").orElse("-"));            //如果空,則返回 指定方法,或者程式碼          System.out.println(Optional.ofNullable(null).orElseGet(()->{              return "hahah";          }));          System.out.println(Optional.ofNullable("1").orElseGet(()->{              return "hahah";          }));            //如果空,則可以拋出異常          System.out.println(Optional.ofNullable("1").orElseThrow(()->{              throw new RuntimeException("ss");          }));      //        Objects.requireNonNull(null,"is null");              //利用 Optional 進行多級判斷          EarthModel earthModel1 = new EarthModel();          //old          if (earthModel1!=null){              if (earthModel1.getTea()!=null){                  //...              }          }          //new          Optional.ofNullable(earthModel1)                  .map(EarthModel::getTea)                  .map(TeaModel::getType)                  .isPresent();      //        Optional<EarthModel> earthModel = Optional.ofNullable(new EarthModel());  //        Optional<List<PersonModel>> personModels = earthModel.map(EarthModel::getPersonModels);  //        Optional<Stream<String>> stringStream = personModels.map(per -> per.stream().map(PersonModel::getName));              //判斷對象中的list          Optional.ofNullable(new EarthModel())                  .map(EarthModel::getPersonModels)                  .map(pers->pers                          .stream()                          .map(PersonModel::getName)                          .collect(toList()))                  .ifPresent(per-> System.out.println(per));              List<PersonModel> models=Data.getData();          Optional.ofNullable(models)                  .map(per -> per                          .stream()                          .map(PersonModel::getName)                          .collect(toList()))                  .ifPresent(per-> System.out.println(per));        }

並發

1、stream替換成parallelStream或 parallel

2、輸入流的大小並不是決定並行化是否會帶來速度提升的唯一因素,性能還會受到編寫程式碼的方式和核的數量的影響

3、影響性能的五要素是:數據大小、源數據結構、值是否裝箱、可用的CPU核數量,以及處理每個元素所花的時間

 //根據數字的大小,有不同的結果      private static int size=10000000;      public static void main(String[] args) {          System.out.println("-----------List-----------");          testList();          System.out.println("-----------Set-----------");          testSet();      }        /**       * 測試list       */      public static void testList(){          List<Integer> list = new ArrayList<>(size);          for (Integer i = 0; i < size; i++) {              list.add(new Integer(i));          }            List<Integer> temp1 = new ArrayList<>(size);          //老的          long start=System.currentTimeMillis();          for (Integer i: list) {              temp1.add(i);          }          System.out.println(+System.currentTimeMillis()-start);            //同步          long start1=System.currentTimeMillis();          list.stream().collect(Collectors.toList());          System.out.println(System.currentTimeMillis()-start1);            //並發          long start2=System.currentTimeMillis();          list.parallelStream().collect(Collectors.toList());          System.out.println(System.currentTimeMillis()-start2);      }        /**       * 測試set       */      public static void testSet(){          List<Integer> list = new ArrayList<>(size);          for (Integer i = 0; i < size; i++) {              list.add(new Integer(i));          }            Set<Integer> temp1 = new HashSet<>(size);          //老的          long start=System.currentTimeMillis();          for (Integer i: list) {              temp1.add(i);          }          System.out.println(+System.currentTimeMillis()-start);            //同步          long start1=System.currentTimeMillis();          list.stream().collect(Collectors.toSet());          System.out.println(System.currentTimeMillis()-start1);            //並發          long start2=System.currentTimeMillis();          list.parallelStream().collect(Collectors.toSet());          System.out.println(System.currentTimeMillis()-start2);      }

調試

1、list.map.fiter.map.xx 為鏈式調用,最終調用collect(xx)返回結果

2、分惰性求值和及早求值

3、判斷一個操作是惰性求值還是及早求值很簡單:只需看它的返回值。如果返回值是 Stream,那麼是惰性求值;如果返回值是另一個值或為空,那麼就是及早求值。使用這些操作的理想方式就是形成一個惰性求值的鏈,最後用一個及早求值的操作返回想要的結果。

4、通過peek可以查看每個值,同時能繼續操作流

private static void peekTest() {          List<PersonModel> data = Data.getData();            //peek列印出遍歷的每個per          data.stream().map(per->per.getName()).peek(p->{              System.out.println(p);          }).collect(toList());      }

作者:我是你的小眼睛兒

鏈接:https://www.jianshu.com/p/9fe8632d0bc2

End