天天看點

stream distinct去重_再來看看Java的新特性——Stream流

stream distinct去重_再來看看Java的新特性——Stream流
半年前開始試着使用Java的新特性,給我印象最深的就是Stream流和Optional。其中Stream提高了看法效率,讓代碼看起來十厘清爽。

為什麼要使用流?

摘要中已經說明了,為了提高開發效率。流可以幫助我們高效操作

集合

,流幫助我們通過

流水線

的方式對集合進行

删減

合并

排序

修改

,并最終傳回我們想要的元素資料或統計資料。

流水線

的意思是說,一批元素不需要等待全部元素都完成某步操作,才進行下步操作,而是可以盡早進行下步操作,就好像流水線工廠一樣,這為流高效運作提供了基礎。流還有一個

内部疊代

的概念,就是把for循環顯示疊代隐藏起來了,這樣可以更友善的

并行開發

流的使用

準備

在使用流之前需要做些準備,首先說明兩個概念:

中間操作

終端操作

中間操作

就好比生産工廠中的房間的一步一步元素處理,每步處理之後,裡面的元素樣式或者資料結構可能發生改變了,但是它還是流。

終端操作

就好比産品生産完了,要打包裝箱,變成最終消費者可見的最終形态,此時,它已經是産品,不再是流了。

接着為了示範操作,先模拟幾條資料

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是跳過前多少個元素。

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

map

map映射,上面的filter是将元素篩選,map則是改變元素的樣式。比如,我們想要知道所有小于10塊食物的名字。

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

flatMap

流的合并,可以将多個數組合并操作,這樣傳回元素不是流,而是具體元素本身了。

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

常用的終端方法

allMatch、anyMatch、noneMatch、findFirst和findAny

ble

d

ata-draft-node="block" data-draft-type="table" data-size="normal" data-row-style="normal">

//前三個方法都是傳回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傳回的都是元素。

//關于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)
           

常用流彙總

stream distinct去重_再來看看Java的新特性——Stream流

其中沒有展示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

類型。

繼續閱讀