Java8 Lambda/Stream使用說明

1、Stream流
1. 流的基本概念數組

1.1 什麼是流?
流是Java8引入的全新概念,它用來處理集合中的數據,暫且能夠把它理解爲一種高級集合。
衆所周知,集合操做很是麻煩,若要對集合進行篩選、投影,須要寫大量的代碼,而流是以聲明的形式操做集合,它就像SQL語句,咱們只需告訴流須要對集合進行什麼操做,它就會自動進行操做,並將執行
結果交給你,無需咱們本身手寫代碼。
所以,流的集合操做對咱們來講是透明的,咱們只需向流下達命令,它就會自動把咱們想要的結果給咱們。因爲操做過程徹底由Java處理,所以它能夠根據當前硬件環境選擇最優的方法處理,咱們也無需編
寫複雜又容易出錯的多線程代碼了。緩存

1.2 流的特色多線程

只能遍歷一次
咱們能夠把流想象成一條流水線,流水線的源頭是咱們的數據源(一個集合),數據源中的元素依次被輸送到流水線上,咱們能夠在流水線上對元素進行各類操做。一旦元素走到了流水線的另外一頭,那麼這些
元素就被「消費掉了」,咱們沒法再對這個流進行操做。固然,咱們能夠從數據源那裏再得到一個新的流從新遍歷一遍。
採用內部迭代方式
若要對集合進行處理,則需咱們手寫處理代碼,這就叫作外部迭代。而要對流進行處理,咱們只需告訴流咱們須要什麼結果,處理過程由流自行完成,這就稱爲內部迭代。app

1.3 流的操做種類
流的操做分爲兩種,分別爲中間操做 和 終端操做。
中間操做
當數據源中的數據上了流水線後,這個過程對數據進行的全部操做都稱爲「中間操做」。
中間操做仍然會返回一個流對象,所以多箇中間操做能夠串連起來造成一個流水線。
終端操做
當全部的中間操做完成後,若要將數據從流水線上拿下來,則須要執行終端操做。
終端操做將返回一個執行結果,這就是你想要的數據。less

1.4 流的操做過程函數

使用流一共須要三步:
準備一個數據源
執行中間操做
中間操做能夠有多個,它們能夠串連起來造成流水線。
執行終端操做
執行終端操做後本次流結束,你將得到一個執行結果。
2. 流的使用
2.1 獲取流spa

在使用流以前,首先須要擁有一個數據源,並經過StreamAPI提供的一些方法獲取該數據源的流對象。數據源能夠有多種形式:
集合
這種數據源較爲經常使用,經過stream()方法便可獲取流對象:
List<Person> list = new ArrayList<Person>();
Stream<Person> stream = list.stream();
數組
經過Arrays類提供的靜態函數stream()獲取數組的流對象:
String[] names = {"chaimm","peter","john"};
Stream<String> stream = Arrays.stream(names);

直接將幾個值變成流對象:
Stream<String> stream = Stream.of("chaimm","peter","john");線程

文件
try(Stream lines = Files.lines(Paths.get(「文件路徑名」),Charset.defaultCharset())){
//可對lines作一些操做
}catch(IOException e){
}
PS:Java7簡化了IO操做,把打開IO操做放在try後的括號中便可省略關閉IO的代碼。
2.2 篩選filtercode

filter函數接收一個Lambda表達式做爲參數,該表達式返回boolean,在執行過程當中,流將元素逐一輸送給filter,並篩選出執行結果爲true的元素。
如,篩選出全部學生:對象

List<Person> result = 

Predicate<String> startWith = (n) -> n.startsWith("b");

Predicate<String> lengthWith = (n) -> n.length() ==3;

list.stream().filter(startWith.and(lengthWith)).forEach((n) -> System.out.println("stream:"+n));

2.3 去重distinct
去掉重複的結果:
List<Person> result = list.stream().distinct().collect(toList());
2.4 截取
截取流的前N個元素:
List<Person> result = list.stream().limit(3).collect(toList());
2.5 跳過
跳過流的前n個元素:
List<Person> result = list.stream()
.skip(3)
.collect(toList());
2.6 映射
對流中的每一個元素執行一個函數,使得元素轉換成另外一種類型輸出。流會將每個元素輸送給map函數,並執行map中的Lambda表達式,最後將執行結果存入一個新的流中。
如,獲取每一個人的姓名(實則是將Perosn類型轉換成String類型):

List<Person> result = list.stream().map(Person::getName).collect(toList());
2.7 合併多個流

例:列出List中各不相同的單詞,List集合以下:
List<String> list = new ArrayList<String>();
list.add("I am a boy");
list.add("I love the girl");
list.add("But the girl loves another girl");

思路以下:
首先將list變成流:
list.stream();
按空格分詞:
list.stream().map(line->line.split(" "));
分完詞以後,每一個元素變成了一個String[]數組。
將每一個String[]變成流:
list.stream().map(line->line.split(" ")).map(Arrays::stream)
此時一個大流裏面包含了一個個小流,咱們須要將這些小流合併成一個流。
將小流合併成一個大流:
用flagmap替換剛纔的map
list.stream().map(line->line.split(" ")).flagmap(Arrays::stream)

去重
list.stream().map(line->line.split(" ")).flagmap(Arrays::stream).distinct().collect(toList());

2.8 是否匹配任一元素:anyMatch

anyMatch用於判斷流中是否存在至少一個元素知足指定的條件,這個判斷條件經過Lambda表達式傳遞給anyMatch,執行結果爲boolean類型。
如,判斷list中是否有學生:
boolean result = list.stream().anyMatch(Person::isStudent);

2.9 是否匹配全部元素:allMatch

allMatch用於判斷流中的全部元素是否都知足指定條件,這個判斷條件經過Lambda表達式傳遞給anyMatch,執行結果爲boolean類型。
如,判斷是否全部人都是學生:
boolean result = list.stream().allMatch(Person::isStudent);

2.10 是否未匹配全部元素:noneMatch

noneMatch與allMatch偏偏相反,它用於判斷流中的全部元素是否都不知足指定條件:
boolean result = list.stream().noneMatch(Person::isStudent);

2.11 獲取任一元素findAny

findAny可以從流中隨便選一個元素出來,它返回一個Optional類型的元素。
Optional<Person> person = list.stream()
.findAny();
Optional介紹
Optional是Java8新加入的一個容器,這個容器只存1個或0個元素,它用於防止出現NullpointException,它提供以下方法:
isPresent()
判斷容器中是否有值。
ifPresent(Consume lambda)
容器若不爲空則執行括號中的Lambda表達式。
T get()
獲取容器中的元素,若容器爲空則拋出NoSuchElement異常。
T orElse(T other)
獲取容器中的元素,若容器爲空則返回括號中的默認值。
2.12 獲取第一個元素findFirst

Optional<Person> person = list.stream().findFirst();
2.13 歸約

歸約是將集合中的全部元素通過指定運算,摺疊成一個元素輸出,如:求最值、平均數等,這些操做都是將一個集合的元素摺疊成一個元素輸出。
在流中,reduce函數能實現歸約。
reduce函數接收兩個參數:

初始值
進行歸約操做的Lambda表達式
2.13.1 元素求和:自定義Lambda表達式實現求和
例:計算全部人的年齡總和
int age = list.stream().reduce(0, (person1,person2)->person1.getAge()+person2.getAge());
reduce的第一個參數表示初試值爲0;
reduce的第二個參數爲須要進行的歸約操做,它接收一個擁有兩個參數的Lambda表達式,reduce會把流中的元素兩兩輸給Lambda表達式,最後將計算出累加之和。

2.13.2 元素求和:使用Integer.sum函數求和

上面的方法中咱們本身定義了Lambda表達式實現求和運算,若是當前流的元素爲數值類型,那麼可使用Integer提供了sum函數代替自定義的Lambda表達式,如:
int age = list.stream().reduce(0, Integer::sum);
Integer類還提供了min、max等一系列數值操做,當流中元素爲數值類型時能夠直接使用。

2.14 數值流的使用
採用reduce進行數值操做會涉及到基本數值類型和引用數值類型之間的裝箱、拆箱操做,所以效率較低。
當流操做爲純數值操做時,使用數值流能得到較高的效率。

2.14.1 將普通流轉換成數值流
StreamAPI提供了三種數值流:IntStream、DoubleStream、LongStream,也提供了將普通流轉換成數值流的三種方法:mapToInt、mapToDouble、mapToLong。
如,將Person中的age轉換成數值流:
IntStream stream = list.stream().mapToInt(Person::getAge);

2.14.2 數值計算
每種數值流都提供了數值計算函數,如max、min、sum等。
如,找出最大的年齡:

OptionalInt maxAge = list.stream().mapToInt(Person::getAge).max();
3. 因爲數值流可能爲空,而且給空的數值流計算最大值是沒有意義的,所以max函數返回OptionalInt,它是Optional的一個子類,可以判斷流是否爲空,並對流爲空的狀況做相應的處理。
此外,mapToInt、mapToDouble、mapToLong進行數值操做後的返回結果分別爲:OptionalInt、OptionalDouble、OptionalLong

4. Collect

4.1 collect是一個終端操做,它接收的參數是將流中的元素累積到彙總結果的各類方式(稱爲收集器)

4.2 預約義收集器包括將流元素歸約和彙總到一個值.以下

工廠方法

返回類型

用於

toList

List<T>

把流中全部元素收集到List中

示例:List<Menu> menus=Menu.getMenus.stream().collect(Collector.toList())

toSet

Set<T>

把流中全部元素收集到Set中,刪除重複項

示例:Set<Menu> menus=Menu.getMenus.stream().collect(Collector.toSet())

toCollection

Collection<T>

把流中全部元素收集到給定的供應源建立的集合中

示例:ArrayList<Menu> menus=Menu.getMenus.stream().collect(Collector.toCollection(ArrayList::new))

Counting

Long

計算流中元素個數

示例:Long count=Menu.getMenus.stream().collect(counting);

SummingInt

Integer

對流中元素的一個整數屬性求和

示例:Integer count=Menu.getMenus.stream().collect(summingInt(Menu::getCalories))

averagingInt

Double

計算流中元素integer屬性的平均值

示例:Double averaging=Menu.getMenus.stream().collect(averagingInt(Menu::getCalories))

Joining

String

鏈接流中每一個元素的toString方法生成的字符串

示例:String name=Menu.getMenus.stream().map(Menu::getName).collect(joining(「, 」))

maxBy

Optional<T>

一個包裹了流中按照給定比較器選出的最大元素的optional

若是爲空返回的是Optional.empty()

示例:Optional<Menu> fattest=Menu.getMenus.stream().collect(maxBy(Menu::getCalories))

minBy

Optional<T>

一個包裹了流中按照給定比較器選出的最大元素的optional

若是爲空返回的是Optional.empty()

示例: Optional<Menu> lessest=Menu.getMenus.stream().collect(minBy(Menu::getCalories))

Reducing

歸約操做產生的類型

從一個做爲累加器的初始值開始,利用binaryOperator與流中的元素逐個結合,從而將流歸約爲單個值

示例:int count=Menu.getMenus.stream().collect(reducing(0,Menu::getCalories,Integer::sum));

collectingAndThen

轉換函數返回的類型

包裹另外一個轉換器,對其結果應用轉換函數

示例:Int count=Menu.getMenus.stream().collect(collectingAndThen(toList(),List::size))

groupingBy

Map<K,List<T>>

根據流中元素的某個值對流中的元素進行分組,並將屬性值作爲結果map的鍵

示例:Map<Type,List<Menu>> menuType=Menu.getMenus.stream().collect(groupingby(Menu::getType))

partitioningBy

Map<Boolean,List<T>>

根據流中每一個元素應用謂語的結果來對項目進行分區

示例:Map<Boolean,List<Menu>> menuType=Menu.getMenus.stream().collect(partitioningBy(Menu::isType));

 

4.3 預約義收集器能夠用groupby對流中元素進行分組或者用partitioningBy進行分區

4.4 收集器能夠高效的複合起來,進行多級分組,多級分區和歸約

4.5 能夠本身實現collector接口進行定義本身的收集器

2、總結

1. Stream API 經常使用操做以下:

操做

類型

返回類型

函數式接口

函數描述符

filter

中間

Stream<T>

Predicate<T>

T->Boolean

distinct

中間-有狀態

Stream<T>

 

 

Skip

中間-有狀態

Stream<T>

Long

 

Limit

中間-有狀態

Stream<T>

Long

 

Map

中間

Stream<T>

Function<T,R>

T->R

Flatmap

中間

Stream<T>

Function<T,Stream<R>>

T->Stream<R>

Sorted

中間-有狀態

Stream<T>

Compartor<T>

(T,T)->int

anyMatch

終端

Boolean

Predicate<T>

T->Boolean

noneMatch

終端

Boolean

Predicate<T>

T->Boolean

allMatch

終端

Boolean

Predicate<T>

T->Boolean

findAny

終端

Optional<T>

 

 

findFirst

終端

Optional<T>

 

 

forEach

終端

Void

Consumer<T>

T->void

Collect

終端

R

Collector<T,A,R>

 

Reduce

終端-有狀態

Optional<T>

BinaryOperator<T>

(T,T)->T

Count

終端

Long

 

 

 

2. 可使用filter,distinct,skip和limit對流進行篩選和切片

3. 可使用map和flatMap提取或轉換流中的元素

4. 可使用findFirst和findAny方法查找流中的元素.你能夠用allMatch,noneMatch和anyMatch方法讓流匹配給定的謂語

5. 上述方法都利用了短路:找到結果就馬上中止計算,並無必要處理整個流

6. 能夠利用reduce方法將流中的全部元素迭代合併成一個結果,例如求和或者查詢最大的元素

7. filter和map等操做都是無狀態的,他們並無儲存任何狀態.reduce等操做要儲存狀態才能計算出一個值.sorted和distinct等操做也要儲存狀態,由於他們須要把流中的全部元素緩存起來才能返回一個新的流.這種操做稱爲有狀態操做.

8. 流有三種基本原始類型特化:intStream,doubleStream和LongStream.他們的操做也有相應的特化

9. 流不只能夠從集合建立,也能夠從值,數組,文件以及iterate與generate等特定方法建立

10.Demo案例

public static Map<String, Integer> sumIntByGroup(List<Map> in, String sumKey, String groupKey) {
    return in.stream()
        .collect(
            groupingBy(item -> (String) item.get(groupKey),//根據groupKey字段分組
                TreeMap::new,
                Collectors.summingInt(item -> Integer.parseInt(String.valueOf(item.get(sumKey))))//對sumKey作彙總
            )
        );
  }

  //分組求和
  public static Map<String, Double> sumDoubleByGroupWithGroupFunction(List<Map> in, String sumKey, String groupKey, Function<String, String> groupKeyFunction) {
    return in.stream()
        .collect(
            groupingBy(item -> groupKeyFunction.apply((String) item.get(groupKey)),
                TreeMap::new,
                Collectors.summingDouble(item -> Double.valueOf(String.valueOf(item.get(sumKey))))//對時長作彙總
            )
        );
  }

  //求和
  public static Double sumDouble(List<Map> in, String sumKey) {
    return (Double) in.stream().mapToDouble(item -> Double.valueOf(String.valueOf(item.get(sumKey)))).sum();
  }

  //求和
  public static Integer sumInt(List<Map> in, String sumKey) {
    return (Integer) in.stream().mapToInt(item -> Integer.valueOf(String.valueOf(item.get(sumKey)))).sum();
  }

  //求和
  public static Double sumDoubleWithFunction(List<Map> in, String sumKey, Function<String, String> groupKeyFunction) {
    return (Double) in.stream().mapToDouble(item -> Double.valueOf(String.valueOf(item.get(groupKeyFunction.apply(sumKey))))).sum();
  }

  //分組求列表
  public static Map<String, List<String>> obtainMapByGroupWithGroupFunction
  (List<Map> in, String listKey, String groupKey, Function<String, String> groupKeyFunction) {
    return in.stream()
        .collect(
            groupingBy(item -> groupKeyFunction.apply(String.valueOf(item.get(groupKey))),
                Collectors.mapping(item -> String.valueOf(item.get(listKey)),
                    Collectors.toList())
            )
        );
  }
  

  //分組求列表
  public static Map<String, List<String>> obtainMapByGroupWithGroupFunctionAndWhere
  (List<Map> in, String listKey, String groupKey, Function<String, String> groupKeyFunction, String whereKey, String whereOper, String whereValue) {
    switch (whereOper) {
      case "<=":
        return in.stream().filter(o -> String.valueOf(o.get(whereKey)).compareTo(whereValue) <= 0)
            .collect(
                groupingBy(item -> groupKeyFunction.apply(String.valueOf(item.get(groupKey))),
                    Collectors.mapping(item -> String.valueOf(item.get(listKey)),
                        Collectors.toList())
                )
            );
      case ">=":
        return in.stream().filter(o -> String.valueOf(o.get(whereKey)).compareTo(whereValue) >= 0)
            .collect(
                groupingBy(item -> groupKeyFunction.apply(String.valueOf(item.get(groupKey))),
                    Collectors.mapping(item -> String.valueOf(item.get(listKey)),
                        Collectors.toList())
                )
            );
      case "==":
      default:
        return in.stream().filter(o -> String.valueOf(o.get(whereKey)).compareTo(whereValue) == 0)
            .collect(
                groupingBy(item -> groupKeyFunction.apply(String.valueOf(item.get(groupKey))),
                    Collectors.mapping(item -> String.valueOf(item.get(listKey)),
                        Collectors.toList())
                )
            );
    }
  }

  //分組求列表
  public static Map<String, List<String>> obtainListByGroupWithSetFunction(List<Map> in, String setKey, String groupKey, Function<String, String> setKeyFunction) {
    return in.stream()
        .collect(
            groupingBy(item -> String.valueOf(item.get(groupKey)),
                Collectors.mapping(item -> setKeyFunction.apply(String.valueOf(item.get(setKey))), Collectors.toList())
            )
        );
  }

  //分組求集合
  public static Map<String, Set<String>> obtainSetByGroupWithGroupFunction(List<Map> in, String setKey, String groupKey, Function<String, String> groupKeyFunction) {
    return in.stream()
        .collect(
            groupingBy(item -> groupKeyFunction.apply(String.valueOf(item.get(groupKey))),
                Collectors.mapping(item -> String.valueOf(item.get(setKey)), Collectors.toSet())
            )
        );
  }


  //求惟一值列表
  public static Set<String> obtainSet(List<Map> in, String distinctKey) {
    return in.stream()
        .map(item -> String.valueOf(item.get(distinctKey)))
        .collect(Collectors.toSet());
  }

  
  //求指定key的list集合
  public static List<String> obtainForList(List<JSONObject> in, String listKey) {
    return in.stream()
        .map(item -> String.valueOf(item.get(listKey)))
        .collect(Collectors.toList());
  }
  //求指定key的list集合而且去重
  public static List<String> obtainForListQc(List<JSONObject> in, String listKey) {
    return in.stream()
        .map(item -> String.valueOf(item.get(listKey)))
        .collect(Collectors.toList()).stream().distinct().collect(Collectors.toList());
  }
相關文章
相關標籤/搜索