用了多年Java,你的團隊會使用Java8 Stream API嗎?

Lambda

在介紹Stream以前,咱們先來講說Java8中的另外一個新特性:Lambda,直接上代碼吧java

在Java8以前,list自定義排序會使用匿名內部類,實現本身的排序邏輯程序員

stuList.sort(new Comparator<Student>() {
            @Override
            public int compare(Student stu1, Student stu2) {
                return stu1.getAge().compareTo(stu2.getAge());
            }
        });

下面看一下Lambda表達式的寫法:web

stuList.sort((s1,s2) -> s1.getAge().compareTo(s2.getAge()));

能夠看到,從代碼量和整潔程度來講,Lambda都顯得都簡潔編程

什麼是Lambda

Lambda表達式是表示可傳遞匿名方法的一種簡潔方式,Lambda表達式沒有名稱,可是有參數列表、函數主體、返回類型,還可能有一個能夠拋出的異常列表數組

Lambda的組成

|<----------參數--------->|<-箭頭->|<----------------主體---------------|
(Student stu1,Student stu2)   ->   stu1.getAge().compareTo(stu2.getAge())
  • 參數列表:上例中的兩個參數是傳給匿名方法的
  • 箭頭:鏈接參數和主體,是操做符
  • 主體:是方法的邏輯處理部分,它能夠接收參數列表中的參數

從上面Lambda的組成能夠看出來,Lambda有兩種簡單的語法:微信

  • 參數列表 -> 表達式
  • 參數列表 -> {多個表達式組成的語句}

方法引用

方法引用是Java8中引入的新特性,它提供了一種引用方法而不執行方法的方式,可讓咱們重複使用現用方法的定義,作爲某些Lambda表達式的另外一種更簡潔的寫法併發

好比:dom

stuList.sort((s1,s2) -> s1.getAge().compareTo(s2.getAge()));

可使用方法引用簡寫爲:ide

Comparator comparator = Comparator.comparing(Student::getAge);

當你須要方法引用時,目標引用放在分隔符::前,方法的名稱放在分隔符::後。好比,上面的Student::getAge,就是引用了Student中定義的getAge方法。方法名稱後不須要加括號,由於咱們並無實際調用它。方法引用提升了代碼的可讀性,也使邏輯更加清晰svg

好了,Lambda就簡單的介紹到這裏,更多Lambda的詳細用法能夠直接百度本身學習一下

Stream

Java8中添加一個新的抽象概念叫作Stream,它可使用相似SQL語句的形式來處理Java集合的運算。

Stream提供了一種「鏈式」編程,能把一系列的操做像鏈條同樣串連起來,這種風格的處理方式把集合看做是一種流,流在管道中傳輸,每一步操做均可以看做是管道中的一個節點,全部節點處理完成造成最終的結果

舉個形象的例子,Stream的操做相似於流水線生產,整個產品線從頭至尾造成一條鏈路,每一個環節就是一個處理節點,最終全部節點處理完成就造成了最終的產品

Stream API簡化了集合的各類操做,提升了程序員的生產力,讓程序員能寫出高效率、乾淨、整潔的代碼

流的常見建立方式

public void create() throws FileNotFoundException {
        Stream<String> stringStream = Stream.of("a","b","c");
        Stream<String> stringStream1 = Stream.of("d","e","f");
        //經過concat將兩個Stream鏈接成一個Stream
        Stream<String> stringStream2 = Stream.concat(stringStream,stringStream1);
        stringStream2.forEach(str -> System.out.println(str));
        //經過迭代器建立Stream
        Stream<Integer> integerStream = Stream.iterate(0,(x) -> (x + 1)).limit(10);
        //generate建立Stream
        Stream<Double> doubleStream =  Stream.generate(Math::random).limit(5);

        //經過集合建立stream
        List<String> list = Lists.newArrayList("a","b","c");
        Stream<String> listStream = list.stream();
        Stream<String> parallelStream = list.parallelStream();//併發流

        //數組轉換成Stream
        String[] stringArr = new String[]{"a","b","c"};
        Stream<String> arrStream = Arrays.stream(stringArr);

        //文件流轉換成Stream
        BufferedReader reader = new BufferedReader(new FileReader("D:\\test.txt"));
        Stream<String> fileStream = reader.lines();

    }

Stream中的操做分爲兩類

  • 中間操做
  • 終止操做
|<------Stream------>|       |<-------中間操做--------->|<--終止操做-->|
+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+       +------+   +------+   +---+   +-------+

中間操做

中間操做就是操做以後,Stream仍然是一個Stream,可使用其餘的中間操做繼續對結果進行操做

  • map 傳入一個函數,將list中的全部元素都套用這個函數
  • flatMap 傳入一個函數,將每一個元素轉換成另一個流,再將全部的流鏈接成一個流
  • filter 傳入過濾的規則,對stream中的元素進行過濾
  • limit 傳入一個數字,取stream中前面幾個元素
  • skip 傳入一個數字,跳過幾個元素
  • distinct 去掉重複元素
  • sorted 對元素進行天然排序,當遇到對象排序時,能夠自定義經過對象的屬性來排序
  • peek 傳入一個Consumer,沒有返回值,不改變Stream中原來的元素,注意對比map
map、flatMap用法
public void mapTest(){
        List<Integer> list = Lists.newArrayList(1,2,3,4,5);
        //map就是傳入一個函數,將list中的全部元素都套用這個函數
        Stream<Integer> stream = list.stream().map(x -> x * x);
        stream.forEach(x -> System.out.println(x));

        List<String> flapList = Lists.newArrayList("a#b#c","1#2#3");
        //flatMap就是傳入一個函數,將每一個元素轉換成另一個流,再將全部的流鏈接成一個流
        Stream<String> flagStream = flapList.stream().flatMap(x -> {
           String[] strArr = x.split("#");
           Stream<String> tmpStream = Arrays.stream(strArr);
           return tmpStream;
        });
        flagStream.forEach(x -> System.out.println(x)); //a b c 1 2 3
    }
filter用法
public void filterTest(){
        List<Integer> list = Lists.newArrayList(1,2,3,4,5);
        //filter就是傳入過濾的規則,對stream中的元素進行過濾
        list.stream().filter(x -> x > 3).forEach(x -> System.out.println(x));
    }
limit用法
public void limitTest(){
        List<String> list = Lists.newArrayList("a","b","c","x","y","z");
        //limit傳入一個數字,取stream中前面幾個元素
        list.stream().limit(3).forEach(x -> System.out.println(x));
    }
skip用法
public void skipTest(){
        List<Integer> list = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
        //跳過前三個元素
        list.stream().skip(3);
        //skip配合limit可實現分頁,好比每頁5筆記錄
        list.stream().skip(5 * 0).limit(5);//第一頁
        list.stream().skip(5 * 1).limit(5);//第二頁
        list.stream().skip(5 * 2).limit(5);//第三頁
    }
distinct用法
public void distinctTest(){
        List<String> list = Lists.newArrayList("a","b","c","a","a","c","c","e","f");
        //去掉重複元素
        list.stream().distinct();
    }
sorted用法
public void sortedTest(){
        //sorted是對元素進行天然排序,當遇到對象排序時,能夠自定義經過對象的屬性來排序
        List<String> list = Lists.newArrayList("a","b","c","a","a","c","c","e","f");
        list.stream().sorted();

        List<Integer> intList = Lists.newArrayList(1,5,5,3,9,4,4,7,6);
        intList.stream().sorted();
    }
peek用法
public void peekTest(){
        List<String> list = Lists.newArrayList("a","b","c","a","a","c","c","e","f");
        //peek傳入一個Consumer,沒有返回值,不改變Stream中原來的元素,注意對比map
        list.stream().peek(x -> x = x + "666");
    }

終止操做

終止操做符就是用來對數據進行收集或者消費的,數據到了終止操做這裏就不會向下流動了,終止操做符只能使用一次

  • matchAll 接收一個 Predicate 函數,當流中每一個元素都符合該斷言時才返回true,不然返回false
  • noneMatch 接收一個 Predicate 函數,當流中每一個元素都不符合該斷言時才返回true,不然返回false
  • anyMatch 接收一個 Predicate 函數,只要流中有一個元素知足該斷言則返回true,不然返回false
  • findFirst 返回流中第一個元素
  • findAny 返回流中的任意元素
  • collect 收集操做
  • count 統計操做
  • min、max 最值操做
  • reduce 規約操做。全部的元素規約成一個,好比對全部元素求和,求積等
  • forEach、forEachOrdered 遍歷操做
  • toArray 數組操做
matchAll、noneMatch、anyMatch用法
public void matchTest(){
        List<Integer> list = Lists.newArrayList(12,14,3,24,5,23,77);
        list.stream().allMatch(x -> x > 10); //全部元素知足條件才返回true
        list.stream().noneMatch( x -> x > 100);//全部元素不知足條件才返回true
        list.stream().anyMatch(x -> x > 50);//只有一個元素知足就返回true
    }
findFirst、findAny用法
public void findTest(){
        List<Integer> list = Lists.newArrayList(12,14,3,24,5,23,77);
        list.stream().findFirst().get();//返回Stream中第一個元素
        list.stream().findAny().get();//返回Steam中任意元素
    }
collect用法

將序列的內容從新組織到新的容器中,好比 List 或者 Map

public void collectTest(){
        Student stu1 = new Student("stu1",11);
        Student stu2 = new Student("stu2",12);
        Student stu3 = new Student("stu3",13);
        Student stu4 = new Student("stu4",14);
        Student stu5 = new Student("stu5",15);
        List<Student> list = Lists.newArrayList(stu1,stu2,stu3,stu4,stu5);
        //將list中全部學生的姓名收集起來,放到一個新list中
        List<String> nameList = list.stream().map(Student::getName).collect(Collectors.toList());
        nameList.forEach(System.out::println);
    }

Collectors

Collectors提供了不少種Collector的建立方式

public void CollectorsTest(){
        Student stu1 = new Student("stu1",11);
        Student stu2 = new Student("stu2",12);
        Student stu3 = new Student("stu3",13);
        Student stu4 = new Student("stu4",14);
        Student stu5 = new Student("stu5",15);
        List<Student> list = Lists.newArrayList(stu1,stu2,stu3,stu4,stu5);
        //將全部學生的姓名收集到新的list中
        List<String> nameList = list.stream().map(Student::getName).collect(Collectors.toList());
        //一樣能夠收集到Set中
        Set<String> nameSet = list.stream().map(Student::getName).collect(Collectors.toSet());
        //將每一個學生的name,age轉換成Map,如有名稱相同的會形成key相同致使失敗
        Map<String,Integer> map = list.stream().collect(Collectors.toMap(Student::getName,Student::getAge));
        //字符串鏈接
        String nameJoin = list.stream().map(Student::getName).collect(Collectors.joining(";","(",")"));

        //---聚合操做----
        //count
        Long count = list.stream().collect(Collectors.counting());
        // max
        Integer maxAge = list.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare)).get();
        // sum
        Integer sumAge = list.stream().collect(Collectors.summingInt(Student::getAge));
        // avg
        Double avgAge = list.stream().collect(Collectors.averagingDouble(Student::getAge));
        // 全部聚合操做的返回
        DoubleSummaryStatistics summaryStatistics = list.stream().collect(Collectors.summarizingDouble(Student::getAge));
        summaryStatistics.getSum();
        summaryStatistics.getAverage();
        summaryStatistics.getCount();
        summaryStatistics.getMax();
        summaryStatistics.getMin();

        //group 按年齡分組
         Map<Integer,List<Student>> ageGroup = list.stream().collect(Collectors.groupingBy(Student::getAge));

         //分區,分紅兩部分,一部分大於12歲,一部分小於等於12歲
        Map<Boolean,List<Student>> agePartition = list.stream().collect(Collectors.partitioningBy(x -> x.getAge() > 12));

        //規約
        Integer reducingAge = list.stream().map(Student::getAge).collect(Collectors.reducing(Integer::max)).get();

    }

能夠看到Collectors提供了不少方便的收集方式,讓咱們很方便的處理集合的各類操做

count用法
public void countTest(){
        List<Integer> list = Lists.newArrayList(12,14,3,24,5,23,77);
        Long count = list.stream().count();
    }
min、max用法
public void maxAndminTest(){
        List<Integer> list = Lists.newArrayList(12,14,3,24,5,23,77);
        Integer max = list.stream().max(Integer::compareTo).get();
        Integer min = list.stream().min(Integer::compareTo).get();
    }
reduce用法
public void reduceTest(){
        List<Integer> list = Lists.newArrayList(12,14,3,24,5,23,77);
        Integer sum = list.stream().reduce(0,(x,y) -> x + y);
    }
forEachOrdered用法

適用用於並行流的狀況下進行迭代,能保證迭代的有序性

public void forEachOrderedTest(){
        List<Integer> list = Lists.newArrayList(12,14,3,24,5,23,77);
        list.stream().parallel().forEachOrdered(System.out::println);
    }
toArray用法
public void toArrayTest(){
        List<Integer> list = Lists.newArrayList(12,14,3,24,5,23,77);
        Object[] objects = list.stream().toArray();
        for(Object obj : objects){
            System.out.println(obj);
        }
    }

總結

  • Lambda表達式是表示可傳遞匿名方法的一種簡潔方式
  • 從上面Lambda的組成能夠看出來,Lambda有兩種簡單的語法:
    • 參數列表 -> 表達式
    • 參數列表 -> {多個表達式組成的語句}
  • 方法引用是Java8中引入的新特性,它提供了一種引用方法而不執行方法的方式,可讓咱們重複使用現用方法的定義,作爲某些Lambda表達式的另外一種更簡潔的寫法
  • Java8中添加一個新的抽象概念叫作Stream,它可使用相似SQL語句的形式來處理Java集合的運算
  • Stream API簡化了集合的各類操做,提升了程序員的生產力,讓程序員能寫出高效率、乾淨、整潔的代碼
  • Stream中的操做分爲兩類
    • 中間操做:中間操做就是操做以後,Stream仍然是一個Stream,可使用其餘的中間操做繼續對結果進行操做
      • map 傳入一個函數,將list中的全部元素都套用這個函數
      • flatMap 傳入一個函數,將每一個元素轉換成另一個流,再將全部的流鏈接成一個流
      • filter 傳入過濾的規則,對stream中的元素進行過濾
      • limit 傳入一個數字,取stream中前面幾個元素
      • skip 傳入一個數字,跳過幾個元素
      • distinct 去掉重複元素
      • sorted 對元素進行天然排序,當遇到對象排序時,能夠自定義經過對象的屬性來排序
      • peek 傳入一個Consumer,沒有返回值,不改變Stream中原來的元素,注意對比map
    • 終止操做:終止操做符就是用來對數據進行收集或者消費的,數據到了終止操做這裏就不會向下流動了,終止操做符只能使用一次
      • matchAll 接收一個 Predicate 函數,當流中每一個元素都符合該斷言時才返回true,不然返回false
      • noneMatch 接收一個 Predicate 函數,當流中每一個元素都不符合該斷言時才返回true,不然返回false
      • anyMatch 接收一個 Predicate 函數,只要流中有一個元素知足該斷言則返回true,不然返回false
      • findFirst 返回流中第一個元素
      • findAny 返回流中的任意元素
      • collect 收集操做
      • count 統計操做
      • min、max 最值操做
      • reduce 規約操做。全部的元素規約成一個,好比對全部元素求和,求積等
      • forEach、forEachOrdered 遍歷操做
      • toArray 數組操做

經過這些方法的介紹,相信你對Java8 Stream API有了必定的瞭解,熟練掌握這些方法能使你的代碼更加簡潔。對於Java8這種「鏈式」編程的風格究竟是否應該推薦使用也是見仁見智,網上不少人說Stream API若是寫的很長,確實能夠減小代碼,但這樣會致使邏輯不夠清晰,寫的人很爽,看的人難受。筆者所在技術團隊目前尚未大量使用Stream API,你們仍是習慣於用日常的語法來寫業務邏輯。

你所在的團隊會大量的用到Stream API來減小代碼量嗎?歡迎給我留言~

若是感受對你有些幫忙,請收藏好,你的關注和點贊是對我最大的鼓勵!
若是想跟我一塊兒學習,堅信技術改變世界,請微信搜索【Java天堂】關注我,我會按期分享本身的學習成果,第一時間推送給您

參考:

https://blog.csdn.net/y_k_y/article/details/84633001

https://www.jianshu.com/p/11c925cdba50