再來看看Java的新特性——Stream流

半年前開始試着使用Java的新特性,給我印象最深的就是Stream流和Optional。其中Stream提升了見解效率,讓代碼看起來十分清爽。java

爲何要使用流?

摘要中已經說明了,爲了提升開發效率。流能夠幫助咱們高效操做集合,流幫助咱們經過流水線的方式對集合進行刪減合併排序修改,並最終返回咱們想要的元素數據或統計數據。流水線的意思是說,一批元素不須要等待所有元素都完成某步操做,才進行下步操做,而是能夠儘早進行下步操做,就好像流水線工廠同樣,這爲流高效運做提供了基礎。流還有一個內部迭代的概念,就是把for循環顯示迭代隱藏起來了,這樣能夠更方便的並行開發json

流的使用

準備

在使用流以前須要作些準備,首先說明兩個概念:中間操做終端操做中間操做就比如生產車間的一步一步元素處理,每步處理以後,裏面的元素樣式或者數據結構可能發生改變了,可是它仍是流。終端操做就比如產品生產完了,要打包裝箱,變成最終消費者可見的最終形態,此時,它已是產品,再也不是流了。數組

接着爲了演示操做,先模擬幾條數據bash

List<JSONObject> menu = new ArrayList<>();
menu.add(new JSONObject().putOpt("name","宮保雞丁").putOpt("price","28"));
menu.add(new JSONObject().putOpt("name","魚香肉絲").putOpt("price","30"));
menu.add(new JSONObject().putOpt("name","肉夾饃").putOpt("price","6"));
menu.add(new JSONObject().putOpt("name","煎餅").putOpt("price","6"));
複製代碼

經常使用的中間操做

filter

filter應該是Stream操做裏面最多見的了,過濾器顧名思義就是過濾數據用的,filter的參數能夠是lambda表達式。數據結構

//好比下面這句話,就是獲得全部價格小於10的食物,獲得的仍是流。
//stream()方法將集合轉成流
menu.stream().filter(jsonObject -> jsonObject.getInt("price")<10);
複製代碼

distinct 、 limit 和 skip

distinct是去重,limit是截取前幾個元素,skip是跳過前多少個元素。spa

List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(1);
integerList.add(2);
integerList.add(3);
integerList.stream()
    .distinct()//通過去重,流還剩一、二、3
    .skip(1)//跳過第一個元素,流中還有二、3
    .limit(1);//截取第一個元素,流中還剩2
複製代碼

map

map映射,上面的filter是將元素篩選,map則是改變元素的樣式。好比,咱們想要知道全部小於10塊食物的名字。code

menu.stream()
    .filter(jsonObject -> jsonObject.getInt("price")<10)//此時仍是jsonObject
	.map(jsonObject -> jsonObject.getStr("name"));//此時變成了String
複製代碼

flatMap

流的合併,能夠將多個數組合並操做,這樣返回元素不是流,而是具體元素自己了。orm

Stream.of(menu,foreignMenu)//此時元素是流 List<Stream>
    .flatMap(x -> x.stream())//此時元素是jsonObject List<jsonObject>
    .map(jsonObject -> jsonObject.getStr("name"))
    .distinct();
複製代碼

經常使用的終端方法

allMatch、anyMatch、noneMatch、findFirst和findAny

方法 含義
allMatch 檢查謂詞是否匹配全部元素
anyMatch 檢查謂詞是否至少匹配一個元素
noneMatch 確保流中沒有任何元素與給定的謂詞匹配
findFirst 查找第一個符合條件的元素
findAny 將返回當前流中的任意元素
//前三個方法都是返回boolean類型
boolean allMatchBool = menu.stream()
    .allMatch(jsonObject -> jsonObject.getInt("price") < 10);
boolean noneMatchBool = menu.stream()
    .noneMatch(jsonObject -> jsonObject.getInt("price") < 10);
boolean anyMatchBool = menu.stream()
    .anyMatch(jsonObject -> jsonObject.getInt("price") < 10);
複製代碼

上面個方法返回的都是boolean類型,findFirst、findAny返回的都是元素。cdn

//關於Optional,先不關心,總之是元素就對了
Optional<JSONObject> first = menu.stream().findFirst();
Optional<JSONObject> any = menu.stream().findAny();

System.out.println(first.get().toString());
System.out.println(any.get().toString());

//輸出
//{"price":"28","name":"宮保雞丁"}
//{"price":"28","name":"宮保雞丁"}
複製代碼

以上兩個方法,只要找到符合條件的數據,流就提早結束了。爲何都是輸出第一個元素,卻要實現有兩個方法呢?由於並行,findAny在並行方面限制會少一些。對象

reduce

最開始的時候說了,最終的返回值能夠是元素集合,也能夠是統計數據(或者說概括),好比說元素求和。假設咱們須要menu中全部食品各要一份須要花多少錢。

Optional<Integer> price = menu.stream()//List<JsonObject>
    .map(jsonObject -> jsonObject.getInt("price"))//先將元素轉成數字List<Integer>
    .reduce((x, y) -> x + y);
System.out.println(price.get());
複製代碼

max和min

這個好理解,就是最大值和最小值嘛。效果相似於

.reduce(Integer::max)
.reduce(Integer::min)
複製代碼

經常使用流彙總

其中沒有展現sorted、count這個都好理解。至於collect這個後面講,用的比較多。

流的轉化

除了對象流(Stream)之外,還有一些類型流,好比說 IntStream(以 IntStream 舉例,其餘相似)上面求和返回的是Optional對象,那能夠直接返回Integer類型嗎?

//使用映射方法mapToInt()就ok了
int price = menu.stream()//Stream
    .mapToInt(jsonObject -> jsonObject.getInt("price"))//IntStream
    .sum();
//類型流轉化回對象流,可使用boxed()
IntStream intStream = menu.stream()
	.mapToInt(jsonObject -> jsonObject.getInt("price"));
Stream<Integer> boxed = intStream.boxed();
//固然了IntStream中有不少int類型操做的方法,就不一一舉例了,源碼打開一看,見名知意
複製代碼

收集器

前面講的經常使用的中間操做,返回值都是流,還有一些中斷操做,返回值都是Optional或者數值。可別忘了Stream最開始的初衷是爲了解決集合操做問題。最終轉化成集合使用的中斷操做collect,參數是接口 Collector,裏面有衆多轉化方法。

轉換成集合

最經常使用的莫非toList() 這個方法了,將返回結果變成List。

List<JSONObject> list = menu.stream()
    .filter(jsonObject -> jsonObject.getInt("price") < 10)
    .collect(Collectors.toList());
//固然還有toSet()等等,舉一反三
複製代碼

字符串拼接

比較經常使用,就是字符串連接了。使用joining()方法

String s = menu.stream()
    .filter(jsonObject -> jsonObject.getInt("price") < 10)
    .map(jsonObject -> jsonObject.getStr("name"))
    .collect(Collectors.joining(","));
複製代碼

分組

根據提供的屬性分組,使用 groupingBy() ,爲了方便說明,給上面各類食品一個type值:

List<JSONObject> menu = new ArrayList<>();
menu.add(new JSONObject().putOpt("name","宮保雞丁").putOpt("price","28").putOpt("type","good"));
menu.add(new JSONObject().putOpt("name","魚香肉絲").putOpt("price","30").putOpt("type","good"));
menu.add(new JSONObject().putOpt("name","肉夾饃").putOpt("price","6").putOpt("type","normal"));
menu.add(new JSONObject().putOpt("name","煎餅").putOpt("price","6").putOpt("type","normal"));

Map<String, List<JSONObject>> type = menu.stream()
    .collect(Collectors.groupingBy(jsonObject -> jsonObject.getStr("type")));
System.out.println(type);
//輸出
//{normal=[{"price":"6","name":"肉夾饃","type":"normal"}, {"price":"6","name":"煎餅","type":"normal"}], good=[{"price":"28","name":"宮保雞丁","type":"good"}, {"price":"30","name":"魚香肉絲","type":"good"}]}
複製代碼

與分組相似的還有一個方法 partitioningBy (),分區,不過它的參數位因而boolean類型。

個人公衆號

個人公衆號用於博客同步

相關文章
相關標籤/搜索