List的Stream流操作

Stream流

Stream 中文稱為 “流”,通過(guò)將集合轉(zhuǎn)換為這么一種叫做 “流” 的元素序列,通過(guò)聲明性方式格嗅,能夠?qū)现械拿總€(gè)元素進(jìn)行一系列并行或串行的流水線操作捏雌。

函數(shù)式編程帶來(lái)的好處尤為明顯跃赚。這種代碼更多地表達(dá)了業(yè)務(wù)邏輯的意圖,而不是它的實(shí)現(xiàn)機(jī)制性湿。易讀的代碼也易于維護(hù)纬傲、更可靠、更不容易出錯(cuò)肤频。

面對(duì)一對(duì)多結(jié)構(gòu)叹括,查詢主實(shí)體時(shí)需要附帶主實(shí)體的子實(shí)體列表怎么寫(xiě)?查出主列表宵荒,循環(huán)差子列表

List的Stream流操作可以簡(jiǎn)化我們的代碼汁雷,減少程序運(yùn)行的壓力,應(yīng)對(duì)上面的問(wèn)題报咳,以前的話是先查出對(duì)應(yīng)的list數(shù)據(jù)侠讯,然后根據(jù)取到集合中id去查找對(duì)應(yīng)的子實(shí)體中數(shù)據(jù),接著在放入對(duì)應(yīng)的集合中去少孝,key值表示主實(shí)體的id继低,value值表示對(duì)應(yīng)主實(shí)體id查到的結(jié)合數(shù)據(jù),這樣就會(huì)三次foreach循環(huán)組裝數(shù)據(jù)稍走,會(huì)很麻煩袁翁,當(dāng)數(shù)據(jù)量大的時(shí)候,會(huì)增加程序運(yùn)行的負(fù)荷婿脸,造成運(yùn)行緩慢粱胜。所以,流式操作代替我們的這一堆操作狐树,提高了代碼的簡(jiǎn)易性焙压,可維護(hù)性,可靠性,更不容易出錯(cuò)涯曲。

image.png

列子
首先我們先創(chuàng)建一個(gè) Person 泛型的 List

List<Person> list = new ArrayList<>();
list.add(new Person("jack", 20));
list.add(new Person("mike", 25));
list.add(new Person("tom", 30));

Person 類包含年齡和姓名兩個(gè)成員變量

private String name;
private int age;
  1. stream() / parallelStream()
    最常用到的方法野哭,將集合轉(zhuǎn)換為流
List list = new ArrayList();
// return Stream<E>
list.stream();

而 parallelStream() 是并行流方法,能夠讓數(shù)據(jù)集執(zhí)行并行操作

  1. filter(T -> boolean)
    保留 boolean 為 true 的元素
保留年齡為 20 的 person 元素
list = list.stream()
            .filter(person -> person.getAge() == 20)
            .collect(toList());

打印輸出 [Person{name='jack', age=20}]

collect(toList()) 可以把流轉(zhuǎn)換為 List 類型

  1. distinct()
    去除重復(fù)元素幻件,這個(gè)方法是通過(guò)類的 equals 方法來(lái)判斷兩個(gè)元素是否相等的
    如例子中的 Person 類拨黔,需要先定義好 equals 方法,不然類似[Person{name='jack', age=20}, Person{name='jack', age=20}] 這樣的情況是不會(huì)處理的
  2. sorted() / sorted((T, T) -> int)
    如果流中的元素的類實(shí)現(xiàn)了 Comparable 接口绰沥,即有自己的排序規(guī)則篱蝇,那么可以直接調(diào)用 sorted() 方法對(duì)元素進(jìn)行排序,如 Stream<Integer>
    反之, 需要調(diào)用 sorted((T, T) -> int) 實(shí)現(xiàn) Comparator 接口
根據(jù)年齡大小來(lái)比較:
list = list.stream()
           .sorted((p1, p2) -> p1.getAge() - p2.getAge())
           .collect(toList());

當(dāng)然這個(gè)可以簡(jiǎn)化為

list = list.stream()
           .sorted(Comparator.comparingInt(Person::getAge))
           .collect(toList());
  1. limit(long n)
    返回前 n 個(gè)元素
list = list.stream()
            .limit(2)
            .collect(toList());

打印輸出 [Person{name='jack', age=20}, Person{name='mike', age=25}]
  1. skip(long n)
    去除前 n 個(gè)元素
list = list.stream()
            .skip(2)
            .collect(toList());

打印輸出 [Person{name='tom', age=30}]

tips:

用在 limit(n) 前面時(shí)徽曲,先去除前 m 個(gè)元素再返回剩余元素的前 n 個(gè)元素
limit(n) 用在 skip(m) 前面時(shí)零截,先返回前 n 個(gè)元素再在剩余的 n 個(gè)元素中去除 m 個(gè)元素

list = list.stream()
            .limit(2)
            .skip(1)
            .collect(toList());

打印輸出 [Person{name='mike', age=25}]
  1. map(T -> R)
    將流中的每一個(gè)元素 T 映射為 R(類似類型轉(zhuǎn)換)
List<String> newlist = list.stream().map(Person::getName).collect(toList());

newlist 里面的元素為 list 中每一個(gè) Person 對(duì)象的 name 變量

  1. flatMap(T -> Stream<R>)
    將流中的每一個(gè)元素 T 映射為一個(gè)流,再把每一個(gè)流連接成為一個(gè)流
List<String> list = new ArrayList<>();
list.add("aaa bbb ccc");
list.add("ddd eee fff");
list.add("ggg hhh iii");

list = list.stream().map(s -> s.split(" ")).flatMap(Arrays::stream).collect(toList());

上面例子中秃臣,我們的目的是把 List 中每個(gè)字符串元素以" "分割開(kāi)涧衙,變成一個(gè)新的 List<String>。
首先 map 方法分割每個(gè)字符串元素甜刻,但此時(shí)流的類型為 Stream<String[ ]>绍撞,因?yàn)?split 方法返回的是 String[ ] 類型;所以我們需要使用 flatMap 方法得院,先使用Arrays::stream將每個(gè) String[ ] 元素變成一個(gè) Stream<String> 流傻铣,然后 flatMap 會(huì)將每一個(gè)流連接成為一個(gè)流,最終返回我們需要的 Stream<String>

  1. anyMatch(T -> boolean)
    流中是否有一個(gè)元素匹配給定的 T -> boolean 條件
    是否存在一個(gè) person 對(duì)象的 age 等于 20:
boolean b = list.stream().anyMatch(person -> person.getAge() == 20);
  1. allMatch(T -> boolean)
    流中是否所有元素都匹配給定的 T -> boolean 條件
  2. noneMatch(T -> boolean)
    流中是否沒(méi)有元素匹配給定的 T -> boolean 條件
  3. findAny() 和 findFirst()

findAny():找到其中一個(gè)元素 (使用 stream() 時(shí)找到的是第一個(gè)元素祥绞;使用 parallelStream() 并行時(shí)找到的是其中一個(gè)元素)
findFirst():找到第一個(gè)元素

值得注意的是非洲,這兩個(gè)方法返回的是一個(gè) Optional<T> 對(duì)象,它是一個(gè)容器類蜕径,能代表一個(gè)值存在或不存在两踏,這個(gè)后面會(huì)講到

  1. reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)
    用于組合流中的元素,如求和兜喻,求積梦染,求最大值等
計(jì)算年齡總和:
int sum = list.stream().map(Person::getAge).reduce(0, (a, b) -> a + b);
與之相同:
int sum = list.stream().map(Person::getAge).reduce(0, Integer::sum);

其中,reduce 第一個(gè)參數(shù) 0 代表起始值為 0朴皆,lambda (a, b) -> a + b 即將兩值相加產(chǎn)生一個(gè)新值
同樣地:

計(jì)算年齡總乘積:
int sum = list.stream().map(Person::getAge).reduce(1, (a, b) -> a * b);

當(dāng)然也可以

Optional<Integer> sum = list.stream().map(Person::getAge).reduce(Integer::sum);

即不接受任何起始值帕识,但因?yàn)闆](méi)有初始值,需要考慮結(jié)果可能不存在的情況遂铡,因此返回的是 Optional 類型

  1. count()
    返回流中元素個(gè)數(shù)肮疗,結(jié)果為 long 類型
  2. collect()
    收集方法,我們很常用的是 collect(toList())扒接,當(dāng)然還有 collect(toSet()) 等伪货,參數(shù)是一個(gè)收集器接口们衙,這個(gè)后面會(huì)另外講
  3. forEach()
    返回結(jié)果為 void,很明顯我們可以通過(guò)它來(lái)干什么了碱呼,比方說(shuō):
### 16. unordered()
還有這個(gè)比較不起眼的方法蒙挑,返回一個(gè)等效的無(wú)序流,當(dāng)然如果流本身就是無(wú)序的話愚臀,那可能就會(huì)直接返回其本身

打印各個(gè)元素:
list.stream().forEach(System.out::println);

再比如說(shuō) MyBatis 里面訪問(wèn)數(shù)據(jù)庫(kù)的 mapper 方法:

向數(shù)據(jù)庫(kù)插入新元素:
list.stream().forEach(PersonMapper::insertPerson);

文章出處
作者:Howie_Y
鏈接:http://www.reibang.com/p/0bb4daf6c800
來(lái)源:簡(jiǎn)書(shū)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末脆荷,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子懊悯,更是在濱河造成了極大的恐慌,老刑警劉巖梦皮,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炭分,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡剑肯,警方通過(guò)查閱死者的電腦和手機(jī)捧毛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)让网,“玉大人呀忧,你說(shuō)我怎么就攤上這事±6茫” “怎么了而账?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)因篇。 經(jīng)常有香客問(wèn)我泞辐,道長(zhǎng),這世上最難降的妖魔是什么竞滓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任咐吼,我火速辦了婚禮,結(jié)果婚禮上商佑,老公的妹妹穿的比我還像新娘锯茄。我一直安慰自己,他們只是感情好茶没,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布肌幽。 她就那樣靜靜地躺著,像睡著了一般礁叔。 火紅的嫁衣襯著肌膚如雪牍颈。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天琅关,我揣著相機(jī)與錄音煮岁,去河邊找鬼讥蔽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛画机,可吹牛的內(nèi)容都是我干的冶伞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼步氏,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼响禽!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起荚醒,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤芋类,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后界阁,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體侯繁,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年泡躯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贮竟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡较剃,死狀恐怖咕别,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情写穴,我是刑警寧澤惰拱,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站啊送,受9級(jí)特大地震影響弓颈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜删掀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一翔冀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧披泪,春花似錦纤子、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至艾少,卻和暖如春卡乾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缚够。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工幔妨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鹦赎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓误堡,卻偏偏與公主長(zhǎng)得像古话,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锁施,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355