JAVA Stream簡單介紹

Stream庫

Java SE 8 中引入了這樣一個庫: java.util.stream 包,它有助於爲各類數據來源的批量操做創建簡明的、聲明性的表達式。它是一個精心設計的庫,充分利用了Lambda特性,能夠將 Streams 視爲 Java 中第一個充分利用了Lambda表達式的強大功能的庫。java

清單1:原來計算一個列表中除去1的總和的代碼:編程

List<Integer> list = Arrays.asList(1,2,3,4,5);
int sum = 0;
for(Integer i: list){
    if(i == 1){
        continue;
    }
    sum+=i;
}

清單2:使用Stream進行計算的代碼:數組

List<Integer> list = Arrays.asList(1,2,3,4,5);
int sum=list.stream().filter(t->t!=1).mapToInt(t->t).sum();

能夠發現,使用Stream進行計算的代碼更加簡潔,而且可讀性更強數據結構

流的關注點不是數據,而是計算,流具備很強的數據聚合、彙總能力。一個流管道包含一個流來源、0 或多箇中間操做,以及一個終止操做。
使用流進行數據計算能夠大體分爲三個步驟:
一、由數據源構建流管道,即建立一個Stream
二、執行中間流操做
三、執行終止流操做app

  • 中間流操做負責將一個流轉換爲另外一個流,中間流操做是惰性的:它只會設置流管道的下一個階段,並不會啓動任何操做。對數據的處理在執行終止操做時開始,執行終止操做以後,會終止當前流管道,該流管道不能再被使用,若是須要遍歷同一個數據集,須要從新設置一個流管道。
  • 中間操做可進一步劃分爲無狀態(filter()、map()、flatMap())和有狀態(sorted()、limit()、distinct())操做。無狀態操做是可在元素上直接執行而無需知道其餘任何元素的操做。例如,過濾操做只需檢查當前元素來肯定是包含仍是消除它;有狀態操做則表現爲對當前元素的操做會受到其餘元素的影響,如排序操做必須查看全部元素以後才能肯定當前元素的位置順序

構建流管道:ide


Collection.stream()                                使用一個集合的元素建立一個流。
Stream.of(T...)                                    使用傳遞給工廠方法的參數建立一個流。
Stream.of(T[])                                    使用一個數組的元素建立一個流。
Stream.empty()                                    建立一個空流。
Stream.iterate(T first, BinaryOperator<T> f)    建立一個包含序列 first, f(first), f(f(first)), ... 的無限流
Stream.iterate(T first, Predicate<T> test, BinaryOperator<T> f)    (僅限 Java 9)相似於 Stream.iterate(T first, BinaryOperator<T> f),但流在測試預期返回 false 的第一個元素上終止。
Stream.generate(Supplier<T> f)                    使用一個生成器函數建立一個無限流。
IntStream.range(lower, upper)                    建立一個由下限到上限(不含)之間的元素組成的 IntStream。
IntStream.rangeClosed(lower, upper)                建立一個由下限到上限(含)之間的元素組成的 IntStream。
BufferedReader.lines()                            建立一個有來自 BufferedReader 的行組成的流。
BitSet.stream()                                    建立一個由 BitSet 中的設置位的索引組成的 IntStream。
Stream.chars()                                    建立一個與 String 中的字符對應的 IntStream。

中間流操做:函數


filter(Predicate<T>)                與預期匹配的流的元素
map(Function<T, U>)                    將提供的函數應用於流的元素的結果
flatMap(Function<T, Stream<U>>        將提供的流處理函數應用於流元素後得到的流元素
distinct()                            已刪除了重複的流元素
sorted()                            按天然順序排序的流元素
Sorted(Comparator<T>)                按提供的比較符排序的流元素
limit(long)                            截斷至所提供長度的流元素
skip(long)                            丟棄了前 N 個元素的流元素
takeWhile(Predicate<T>)                (僅限 Java 9)在第一個提供的預期不是 true 的元素處階段的流元素
dropWhile(Predicate<T>)                (僅限 Java 9)丟棄了所提供的預期爲 true 的初始元素分段的流元素

清單3:返回年齡小於22歲且身高由低到高排名在11-20名的同窗的名字測試

List<String> names=list.stream().sorted(Comparator.comparing(t->t.getHeight())).filter(t->t.getAge()< 22).map(t->t.getName()).skip(10).limit(10).collect(Collectors.toList());

終止流操做:設計


forEach(Consumer<T> action)            將提供的操做應用於流的每一個元素。
toArray()                            使用流的元素建立一個數組。
reduce(...)                            將流的元素聚合爲一個彙總值。
collect(...)                        將流的元素聚合到一個彙總結果容器中。
min(Comparator<T>)                    經過比較符返回流的最小元素。
max(Comparator<T>)                    經過比較符返回流的最大元素。
count()                                返回流的大小。
{any,all,none}Match(Predicate<T>)    返回流的任何/全部元素是否與提供的預期相匹配。
findFirst()                            返回流的第一個元素(若是有)。
findAny()                            返回流的任何元素(若是有)。

Stream.reduce()方法

聚合操做Stream.reduce()能夠將元素序列聚合到該類型的單個元素值。Stream庫中有多種reduce()方法,包括瞭如下兩種:
Optional<T> reduce(BinaryOperator<T> op)
T reduce(T identity, BinaryOperator<T> op)code

Reduction Operation是一種來自函數編程的技術,它抽象化了許多不一樣的操做。給定一個類型爲 T,包含 n 個元素的非空數列 (x1, x2, ..., xn) 和 T 上的二元運算符(在這裏表示爲&),reduction opreation 被定義爲:(x1&x2&x3...&xn)

二元運算符&須要知足結合性條件:即 ((a&b)&c)==(a&(b&c)),並不須要知足交換性

Optional<T> reduce(BinaryOperator<T> op):只須要肯定二元運算符即定義reduction operation的具體操做便可。

清單5:計算總和

int sum = Stream.of(1,2,3,4,5).reduce((a,b)->a+b;).get();

清單6:計算最大值

int max = Stream.of(1,2,3,4,5).reduce(Math::max).get();

T reduce(T identity, BinaryOperator<T> op):使用這個方法時還須要提供一個identity初始值
清單7:字符串串聯:

String string = strings.stream().reduce("",String::concat);

Stream.collect()方法

Stream.reduce操做是經過某個鏈接動做將全部元素彙總成一個彙總結果,好比獲取數列的和或它的最大值。可是有時咱們並不須要單個彙總值;您想將結果組織爲相似 List 或 Map 的數據結構,或者將它彙總爲多個彙總值。在這種狀況下,可使用collect()方法。

它接受如下 3 個參數:

<R> collect(Supplier<R> resultSupplier,
            BiConsumer<R, T> accumulator, 
            BiConsumer<R, R> combiner)

Supplier<R> resultSupplier:一種生成空結果容器的途徑
BiConsumer<R, T> accumulator:一種將新元素合併到結果容器中的途徑
BiConsumer<R, R> combiner:一種合併兩個結果容器的途徑

清單8:

List<String> names=list.stream().filter(t->t.getAge()>20).map(t->t.getName()).collect(ArrayList::new,ArrayList::add,ArrayList::addAll);

ArrayList::new 建立一個ArrayList的空容器
ArrayList::add 將新元素添加到結果容器中
ArrayList::addAll 合併兩個ArrayList容器

Stream同時還有這樣一個collect()方法:

<R, A> R collect(Collector<? super T, A, R> collector);

Collector做爲參數傳入,配合Collectors使用開發更加簡便,清單8能夠直接調用Collectors.toList()方法

List<String> names=list.stream().filter(t->t.getAge()>20).map(t->t.getName()).collect(Collectors.toList());

Collectors 類常見的聚合操做


toList()                                將元素收集到一個 List 中。
toSet()                                    將元素收集到一個 Set 中。
toCollection(Supplier<Collection>)        將元素收集到一個特定類型的 Collection 中。
toMap(Function<T, K>, Function<T, V>)    將元素收集到一個 Map 中,依據提供的映射函數將元素轉換爲鍵值。
summingInt(ToIntFunction<T>)            計算將提供的 int 值映射函數應用於每一個元素(以及 long 和 double 版本)的結果的總和。
summarizingInt(ToIntFunction<T>)        計算將提供的 int 值映射函數應用於每一個元素(以及 long 和 double 版本)的結果的 sum、min、max、count 和 average。
reducing()                                向元素應用縮減(一般用做下游收集器,好比用於 groupingBy)(各類版本)。
partitioningBy(Predicate<T>)            將元素分爲兩組:爲其保留了提供的預期的組和未保留預期的組。
partitioningBy(Predicate<T>, Collector)    將元素分區,使用指定的下游收集器處理每一個分區。
groupingBy(Function<T,U>)                將元素分組到一個 Map 中,其中的鍵是所提供的應用於流元素的函數,值是共享該鍵的元素列表。
groupingBy(Function<T,U>, Collector)    將元素分組,使用指定的下游收集器來處理與每一個組有關聯的值。
minBy(BinaryOperator<T>)                計算元素的最小值(與 maxBy() 相同)。
mapping(Function<T,U>, Collector)        將提供的映射函數應用於每一個元素,並使用指定的下游收集器(一般用做下游收集器自己,好比用於 groupingBy)進行處理。
joining()                                假設元素爲 String 類型,將這些元素聯結到一個字符串中(或許使用分隔符、前綴和後綴)。
counting()                                計算元素數量。(一般用做下游收集器。)

這裏主要介紹一下Collectors.groupingBy(),它主要用於對元素的分組操做,同時它也能夠經過結合下游收集器實現對元素更加複雜,更加細緻的分組操做

一、groupingBy(Function<T,U>) 將元素分組到一個 Map 中,其中的鍵是所提供的應用於流元素的函數,值是共享該鍵的元素列表。

使用Student.age做爲Map的key,value則是List<Student>

Map<Integer,List<Student>> map=list.stream().collect(Collectors.groupingBy(Student::getAge));

二、groupingBy(Function<T,U>, Collector) 將元素分組,使用指定的下游收集器來處理與每一個組有關聯的值。
將Student.age做爲Map的key,只保留各年齡段身高最高的student

Map<Integer,Optional<Student>> map=list.stream().collect(Collectors.groupingBy(Student::getAge,Collectors.maxBy(Comparator.comparing(Student::getHeight))));

Stream的原理

對於順序執行,Stream構造了一個 Consumer對象鏈,其結構與管道結構相符。其中每一個 Consumer 對象知道下一個階段;當它收到一個元素(或被告知沒有更多元素)時,它會將 0 或多個元素髮送到鏈中的下一個階段。例如,與 filter() 階段有關聯的 Consumer 將過濾器謂詞應用於輸入元素,並將它發送或不發送到下一個階段;與 map() 階段有關聯的 Consumer 將映射函數應用於輸入元素,並將結果發送到下一個階段。與有狀態操做(好比 sorted())有關聯的 Consumer 會緩衝元素,直到它看到輸入的末尾,而後將排序的數據發送到下一個階段。Consumer對象鏈的最後一個階段將實現終止操做。

相關文章
相關標籤/搜索