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ò)涯曲。
列子
首先我們先創(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;
- stream() / parallelStream()
最常用到的方法野哭,將集合轉(zhuǎn)換為流
List list = new ArrayList();
// return Stream<E>
list.stream();
而 parallelStream() 是并行流方法,能夠讓數(shù)據(jù)集執(zhí)行并行操作
- 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 類型
- distinct()
去除重復(fù)元素幻件,這個(gè)方法是通過(guò)類的 equals 方法來(lái)判斷兩個(gè)元素是否相等的
如例子中的 Person 類拨黔,需要先定義好 equals 方法,不然類似[Person{name='jack', age=20}, Person{name='jack', age=20}] 這樣的情況是不會(huì)處理的 - 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());
- limit(long n)
返回前 n 個(gè)元素
list = list.stream()
.limit(2)
.collect(toList());
打印輸出 [Person{name='jack', age=20}, Person{name='mike', age=25}]
- 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}]
- map(T -> R)
將流中的每一個(gè)元素 T 映射為 R(類似類型轉(zhuǎn)換)
List<String> newlist = list.stream().map(Person::getName).collect(toList());
newlist 里面的元素為 list 中每一個(gè) Person 對(duì)象的 name 變量
- 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>
- anyMatch(T -> boolean)
流中是否有一個(gè)元素匹配給定的 T -> boolean 條件
是否存在一個(gè) person 對(duì)象的 age 等于 20:
boolean b = list.stream().anyMatch(person -> person.getAge() == 20);
- allMatch(T -> boolean)
流中是否所有元素都匹配給定的 T -> boolean 條件 - noneMatch(T -> boolean)
流中是否沒(méi)有元素匹配給定的 T -> boolean 條件 - findAny() 和 findFirst()
findAny():找到其中一個(gè)元素 (使用 stream() 時(shí)找到的是第一個(gè)元素祥绞;使用 parallelStream() 并行時(shí)找到的是其中一個(gè)元素)
findFirst():找到第一個(gè)元素
值得注意的是非洲,這兩個(gè)方法返回的是一個(gè) Optional<T> 對(duì)象,它是一個(gè)容器類蜕径,能代表一個(gè)值存在或不存在两踏,這個(gè)后面會(huì)講到
- 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 類型
- count()
返回流中元素個(gè)數(shù)肮疗,結(jié)果為 long 類型 - collect()
收集方法,我們很常用的是 collect(toList())扒接,當(dāng)然還有 collect(toSet()) 等伪货,參數(shù)是一個(gè)收集器接口们衙,這個(gè)后面會(huì)另外講 - 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ū)