java8之StreamAPI

Stream(流):

什麼是stream(流):Stream是一個來自數據源的元素隊列並支持聚合操作
    元素:是特定類型的對象,形成一個隊列。 Java中的Stream並不會存儲元素,而是按需計算
    數據源:流的來源。可以是集合,數組,I/O channel, 產生器generator 等
    聚合操作:類似SQL語句一樣的操作,如:filter, map, reduce, find, match, sorted等
​
Stream操作的兩個基礎特徵:
    Pipelining: 中間操作都會返回流對象本身
        這樣多個操作可以串聯成一個管道,如同流式風格(fluent style)
        這樣做可以對操作進行優化,比如延遲執行(laziness)和短路( short-circuiting)
    內部迭代: 
        以前對集合遍歷都是通過Iterator或者For-Each的方式, 顯式的在集合外部進行迭代,這叫做外部迭代
        Stream提供了內部迭代的方式, 通過訪問者模式(Visitor)實現
注意: 
    1. Stream自己不會存儲元素
    2. Stream不會改變原對象,相反他們會返回一個持有結果的新Stream
    3. Stream操作是延遲執行,這意味着他們會等到需要結果的時候才執行

Stream操作步驟:

1.創建流(即初始化流)
2.中間操作:Intermediate
    一個流可以後面跟隨零個或多個intermediate操作 
    做出某種程度的數據映射/過濾,然後返回一個新的流,交給下一個操作使用
    
    常見中間操作: 
        filter distinct sorted peek limit
        skip parallel sequential unordered
        map (mapToInt, flatMap 等)
3.終止操作:Terminal
    一個流只能有一個terminal操作,執行多個terminal操作會報錯:stream has already been operated upon or closed
    Terminal 操作的執行,纔會真正開始流的遍歷並且會生成一個結果或者一個 side effect
    
    常見終止操作:
        forEach forEachOrdered toArray reduce collect
         min max count anyMatch allMatch noneMatch
         findFirst findAny iterator
    
    短路操作:Short-circuiting
        對於一個intermediate 操作:
            如果它接受的是一個無限大(infinite/unbounded)的Stream,但返回一個有限的新 Stream
        對於一個terminal 操作:
            如果它接受的是一個無限大的 Stream,但能在有限的時間計算出結果
        常見短路操作:
            anyMatch allMatch noneMatch findFirst findAny limit

流的創建:

方式一:Collection接口相關方法 將集合轉換爲流
    default Stream< E> stream();  //返回一個串行流 
    default Stream< E> parallelStream(); //返回一個並行流
    
    示例:
        List<String> list = Arrays.asList("a","ab","abc","abcd","dh","");
        Stream<String> listToStream = list.stream();//串行流
        Stream<String> parallelStream = list.parallelStream();//並行流
        
    並行流通過默認的ForkJoinPool,可能提高你的多線程任務的速度。並行流在遍歷時可能是無序的
    
方式二: Arrays類的靜態方法 stream() 將數組轉換爲流
    public static <T> Stream<T> stream(T[] array)
    
    示例:
        String [] strArray = new String[] {"a", "b", "c","d"};
        Stream<String> arrayToStream = Arrays.stream(strArray);
        
方式三:Stream接口靜態方法    of() 初始化流
    public static<T> Stream<T> of(T... values)
    
    示例:
        Stream<String> stream = Stream.of("a","b","c","d");
​
方式四:Stream接口靜態方法    iterate()、generat()創建無限流    
    public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
    public static<T> Stream<T> generate(Supplier<T> s)
    
    示例:
        //0 2
        Stream<Integer> iterateStream = Stream.iterate(0, (x) -> x+2).limit(2); 
        //0.7959655194696649 0.04986900509440961 0.4258300512930294 0.6815133817071739
        Stream<Double> generateStrem = Stream.generate(Math::random).limit(4);

流的常用操作:

  • 1.篩選/過濾:filter/distinct/limit/skip
方法 描述
filter(Predicate p) 接收 Lambda , 從流中排除某些元素
distinct() 篩選,通過流所生成元素的 hashCode() 和 equals() 去 除重複元素
limit(long maxSize) 截斷流,使其元素不超過給定數量
skip(long n) 跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素 不足 n 個,則返回一個空流。與 limit(n) 互補
    示例:
        Stream<String> strStream = Stream.of("pugb","king","lol","ow","solo","ow","");
        
        strStream
            .filter(s -> s.length()>0)//過濾字符串長度小於0的元素
            .distinct() //元素去重 (需要重寫 hashcode和 equals方法)
            .skip(2)    //跳過流的前兩個元素
            .limit(2)   //從第三個元素起取2個元素
            .forEach(System.out::println);//內部迭代流中的元素
            
    執行結果:lol ow
  • 2.映射:map/flatMap/...
map(Function f) 接收一個函數作爲參數,該函數會被應用到每個元 素上,並將其映射成一個新的元素
mapToDouble(ToDoubleFunction f) 接收一個函數作爲參數,該函數會被應用到每個元 素上,產生一個新的 DoubleStream
mapToInt(ToIntFunction f) 接收一個函數作爲參數,該函數會被應用到每個元 素上,產生一個新的 IntStream
mapToLong(ToLongFunction f) 接收一個函數作爲參數,該函數會被應用到每個元 素上,產生一個新的 LongStream
flatMap(Function f) 接收一個函數作爲參數,將流中的每個值都換成另一個流,然後把所有流連接成一個流
    示例:
        List<String> list = Arrays.asList("a","ab","abc","abcd","dh");
        list.stream()
            .map(String::toUpperCase)//將元素轉爲大寫
            .forEach(System.out::println);//內部迭代流中的元素
       System.out.println("---------------------------------------")
       
       String[] arrayOfWords = {"Hello", "World"};
       List<String> words = Arrays.asList(arrayOfWords);
       words.stream()
            .map(w -> split(""))
            .flatMap(Arrays::stream)
            .distinct()
            .forEach(System.out::println);
     執行結果:
         A AB ABC ABCD DH
         --------------------------------------------------
         H
         l
         o
         W
         r
         d
  • 3.排序:sorted 
方法 描述
sorted() 產生一個新流,其中按自然順序排序
sorted(Comparator comp) 產生一個新流,其中按比較器順序排序
    示例:
        List<Integer> sortList = Arrays.asList(1,2,13,4,15,6,17,8,19);
        sortList.stream()
            .sorted()//排序
            .forEach(System.out::println);//內部迭代流中的元素
            
        sortList.stream()
            .sorted(Integer::compare)//排序
            .forEach(System.out::println);//內部迭代流中的元素
            
    執行結果:1 2 4 6 8 13 15 17 19
  • 4.查找與匹配: 
方法 描述
allMatch(Predicate p) 檢查是否匹配所有元素
anyMatch(Predicate p) 檢查是否至少匹配一個元素
noneMatch(Predicate p) 檢查是否沒有匹配所有元素
findFirst() 返回第一個元素
findAny() 返回當前流中的任意元素
count() 返回流中元素總數
max(Comparator c) 返回流中最大值
min(Comparator c) 返回流中最小值
forEach(Consumer c) 內部迭代(使用 Collection 接口需要用戶去做迭 代,稱爲外部迭代。相反,Stream API 使用內部 迭代——它幫你把迭代做了)
    示例:
        List<String> matchList = Arrays.asList("pugb","king","lol","ow","solo","ow","");
        boolean anyMatch = matchList.stream().anyMatch(str -> str.equals("king"));
        boolean noneMatch = matchList.stream().noneMatch(String::isEmpty);
        boolean allMatch = matchList.stream().allMatch(String::isEmpty);
        System.out.println(anyMatch);   //true
        System.out.println(noneMatch);  //false
        System.out.println(allMatch);   //false
​
        Optional<String> findFirst = matchList.stream().findFirst();
        System.out.println(findFirst.get());    //pugb
        
        Optional<String> findAny = matchList.stream().filter(s -> s.equals("king")).findAny();
        System.out.println(findAny.get());  //king
  •  5.統計:max/min/avg/sum

    示例:
        List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19);
        IntSummaryStatistics stats = integers.stream().mapToInt((x) ->x).summaryStatistics();
        System.out.println("列表中最大的數 : " + stats.getMax());      //19                   
        System.out.println("列表中最小的數 : " + stats.getMin());      //1                    
        System.out.println("所有數之和 : " + stats.getSum());        //85                   
        System.out.println("平均數 : " + stats.getAverage());      // 9.444444444444445   
        System.out.println("=========================================================");
        
        OptionalInt max = integers.stream().mapToInt((x) ->x).max();
        OptionalInt min = integers.stream().mapToInt((x) ->x).min();
        int sum = integers.stream().mapToInt((x) ->x).sum();
        OptionalDouble avg = integers.stream().mapToInt((x) ->x).average();
        System.out.println("最大值: " +max.getAsInt());        //19
        System.out.println("最小值: " +min.getAsInt());        //1
        System.out.println(" 和:"  +sum);                        //85
        System.out.println("平均值: "  +avg.getAsDouble());    // 9.444444444444445
        System.out.println("=========================================================");
        
        //Stream中只有max和min兩個方法 sum/avg需轉換爲對應的包裝類Stream計算
        Optional<Integer> maxInteger = integers.stream().max(Integer::compare);
        Optional<Integer> minInteger = integers.stream().min(Integer::compare);
        System.out.println(maxInteger.get());       //19
        System.out.println(minInteger.get());       //1
  •  6.歸約:reduce
方法 描述
reduce(T iden, BinaryOperator b) 可以將流中元素反覆結合起來,得到一個值。 返回 T
reduce(BinaryOperator b) 可以將流中元素反覆結合起來,得到一個值。 返回 Optional< T>
    備註:map 和 reduce 的連接通常稱爲 map-reduce 模式,因 Google 用它 來進行網絡搜索而出名
    
    示例:
        //求和
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sum = list.stream().reduce(0, (x, y) -> x + y);
        System.out.println(sum);        //55 
  • 7.收集:collect 
    準備Employee類:重寫hashCode和equals方法
        public class Employee {
            private Integer id;
            private String name;
            private Integer age;
            private Double salary;
            private Integer status;
            
            //省略getter/setter/toString/hashCode/equals方法
        }
    定義集合:
        List<Employee> emps = Arrays.asList(
                  new Employee(101, "林青霞", 28, 9889.99),
                  new Employee(102, "東方不敗", 29, 4329.85),
                  new Employee(103, "周星馳", 40, 1233.88),
                  new Employee(104, "大聖", 500, 5000.44),
                  new Employee(105, "張無忌", 15, 3000.09),
                  new Employee(102, "東方不敗", 29, 4329.85)
          );
    收集示例:
        emps.stream()
            .map(Employee::getName)//將員工姓名轉爲stream
            .collect(Collectors.toList())//將員工姓名stream轉爲List集合
            .forEach(System.out::println);
        emps.stream()
            .map(Employee::getName)//將員工姓名轉爲stream
            .collect(Collectors.toSet())//將員工姓名stream轉爲Set集合
            .forEach(System.out::println);
        emps.stream()
        .map(Employee::getName)//將員工姓名轉爲stream
        .collect(Collectors.toCollection(HashSet::new))//將員工姓名stream轉爲HashSet集合
        .forEach(System.out::println);

        執行結果:
            林青霞 東方不敗 周星馳 大聖 張無忌 東方不敗
            ------------------- 
            周星馳 林青霞 大聖 東方不敗 張無忌 
            ------------------- 
            周星馳 林青霞 大聖 東方不敗 張無忌
    統計示例:
        Long count = emps.stream().collect(Collectors.counting());
        System.out.println("員工個數:" + count);
        Double avgSalary = emps.stream().
                collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println("平均工資:" + avgSalary);
        Double sumSalary= emps.stream().
                collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println("總工資:" + sumSalary);
        Optional<Employee> maxSalary = emps.stream()
                .collect(Collectors.maxBy((x, y) -> Double.compare(x.getSalary(), y.getSalary())));
        System.out.println("最高工資員工:" + maxSalary.get());
        Optional<Double> minSalary = emps.stream()
                 .map(Employee::getSalary)
                 .collect(Collectors.minBy(Double::compare));
        System.out.println("最低工資:" + minSalary.get());
        //統計分析
        DoubleSummaryStatistics doubleSummaryStatistics = emps.stream()
                 .collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println("平均工資:" + doubleSummaryStatistics.getAverage());
        System.out.println("最高工資:" + doubleSummaryStatistics.getMax());
        System.out.println("最低工資:" + doubleSummaryStatistics.getMin());
        System.out.println("總工資:" + doubleSummaryStatistics.getSum());
        //拼接
        String join = emps.stream()
                 .map(Employee::getName)//將員工姓名轉爲stream
                 .collect(Collectors.joining(",", "--", "--"));//拼接符"," 前綴"--" 後綴"--"
        System.out.println(join);
        
        
        執行結果:
            員工個數:6
            平均工資:4630.683333333333
            總工資:27784.1
            最高工資員工:Employee [id=101, name=林青霞, age=28, salary=9889.99, status=null]
            最低工資:1233.88
            平均工資:4630.683333333333
            最高工資:9889.99
            最低工資:1233.88
            總工資:27784.1
            --林青霞,東方不敗,周星馳,大聖,張無忌,東方不敗--
             
    收集分組:
        Map<String, List<Employee>> group = emps.stream()
                .collect(Collectors.groupingBy(Employee::getName));
        System.out.println(group);
        
        執行結果:
            {
             周星馳=[Employee [id=103, name=周星馳, age=40, salary=1233.88, status=null]],
             林青霞=[Employee [id=101, name=林青霞, age=28, salary=9889.99, status=null]], 
             大聖=[Employee [id=104, name=大聖, age=500, salary=5000.44, status=null]],
             東方不敗=[Employee [id=102, name=東方不敗, age=29, salary=4329.85, status=null],                          
                      Employee [id=102, name=東方不敗, age=29, salary=4329.85, status=null]
             ],
             張無忌=[Employee [id=105, name=張無忌, age=15, salary=3000.09, status=null]]}
    多級分組:
        Map<String, Map<String, List<Employee>>> moreGroup = emps.stream()
                .collect(Collectors.groupingBy(Employee::getName, Collectors.groupingBy((e) -> {
                    if (e.getAge() < 30) return "青年";
                    else if (e.getAge() < 50) return "中年";
                    else return "老年";
                })));
        System.out.println(moreGroup);
        
        執行結果:
            {
            周星馳={中年=[Employee [id=103, name=周星馳, age=40, salary=1233.88, status=null]]},             
            林青霞={青年=[Employee [id=101, name=林青霞, age=28, salary=9889.99, status=null]]},             
            大聖={老年=[Employee [id=104, name=大聖, age=500, salary=5000.44, status=null]]}, 
            東方不敗={青年=[Employee [id=102, name=東方不敗, age=29, salary=4329.85, status=null],                           
                          Employee [id=102, name=東方不敗, age=29, salary=4329.85, status=null]]
            }, 
            張無忌={青年=[Employee [id=105, name=張無忌, age=15, salary=3000.09, status=null]]}}

 Collector 接口中方法的實現決定了如何對流執行收集操作(如收 集到 List、Set、Map)。但是 Collectors 實用類 供了很多靜態 方法,可以方便地創建常見收集器實例,具體方法與實例如下表:

方法 返回類型 作用
toList List<T> 把流中元素收集到List
List<Employee> emps= list.stream().collect(Collectors.toList());
toSet Set<T> 把流中元素收集到Set
Set<Employee> emps= list.stream().collect(Collectors.toSet());
toCollection Collection<T> 把流中元素收集到創建的集合
Collection<Employee>emps=list.stream().collect(Collectors.toCollection(ArrayList::new));
counting Long 計算流中元素的個數
long count = list.stream().collect(Collectors.counting());
summingInt Integer 對流中元素的整數屬性求和
inttotal=list.stream().collect(Collectors.summingInt(Employee::getSalary));
averagingInt Double 計算流中元素Integer屬性的平均 值
doubleavg= list.stream().collect(Collectors.averagingInt(Employee::getSalary));
summarizingInt IntSummaryStatistics 收集流中Integer屬性的統計值。 如:平均值
IntSummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
joining String 連接流中每個字符串
String str= list.stream().map(Employee::getName).collect(Collectors.joining());
maxBy Optional<T> 根據比較器選擇最大值
Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
minBy Optional<T> 根據比較器選擇最小值
Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
reducing 歸約產生的類型 從一個作爲累加器的初始值 開始,利用BinaryOperator與 流中元素逐個結合,從而歸 約成單個值
inttotal=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
collectingAndThen 轉換函數返回的類型 包裹另一個收集器,對其結 果轉換函數
inthow= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
groupingBy Map<K, List<T>> 根據某屬性值對流分組,屬 性爲K,結果爲V
Map<Emp.Status, List<Emp>> map= list.stream() .collect(Collectors.groupingBy(Employee::getStatus));
partitioningBy Map<Boolean,List<T>> 根據true或false進行分區
Map<Boolean,List<Emp>>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage));

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章