JDK Streams — 行云流水般地編碼
Streams 簡(jiǎn)介
JDK 8已經(jīng)發(fā)布好一段時(shí)間,在JDK 8中新增了好些開發(fā)人員期許的功能;從性能、開發(fā)效率上都有提升。也引入了很多全新的API ,如 java.util.stream
包下的
Stream及其相關(guān) Stream API。
java.util.stream.Stream與java.io包里的InputStream或OutputStream有本質(zhì)的區(qū)別,它和IO流無關(guān)。Stream API類似于具有Iterable功能的集合類,但其行為和集合類又有所不同,它在操作集合對(duì)象功能上有極致的發(fā)揮,可以非常簡(jiǎn)潔、高效地操作大批量數(shù)據(jù)。另外;普遍受到開發(fā)人員歡迎的函數(shù)式編程也可在Stream API開發(fā)應(yīng)用中體現(xiàn)得淋漓盡致。Stream API充分利用Lambda表達(dá)式的編碼特性和函數(shù)式編程的特點(diǎn);讓代碼看起來更加簡(jiǎn)潔和易讀。編碼起來如行云流水一般。下面就Stream API的一些特點(diǎn)用一個(gè)例子簡(jiǎn)單來體驗(yàn)它的強(qiáng)大和編碼上的享受。
Streams 應(yīng)用
1. 類定義、初始化數(shù)據(jù)
定義一個(gè)Student類,其中包括有一個(gè)全參的構(gòu)造方法和isGzStudent()方法; 這個(gè)方法主要用于判斷學(xué)生的名字中是否包含“廣州“,如果包含則返回true,否則返回false。
接著再寫一個(gè)常規(guī)類StreamRocks,主要用于測(cè)試Stream的各方法。在StreamRocks中初試化了一個(gè)包含Student對(duì)象的集合students,該集合也是之后測(cè)試的數(shù)據(jù)來源,由于數(shù)值不再變動(dòng),所以設(shè)定為final。
2. 代替常規(guī)循環(huán)
以前在輸出集合內(nèi)容時(shí),最常見的做法是使用循環(huán)語句,如for/while等將
集合內(nèi)元素一個(gè)個(gè)遍歷并取出其屬性值輸出。Stream的forEach為這樣的遍歷操作提供了簡(jiǎn)便,配合Lambda表達(dá)式;可如下代替常規(guī)循環(huán):
上面的forEach用于遍歷stream中的對(duì)象,由于接受Lambda表達(dá)式,則再將形參stu對(duì)應(yīng)的Student對(duì)象取出學(xué)生姓名再輸出。
forEach(stu -> System.out.println(stu.getName()))
等價(jià)于
for(Student stu:students){
System.out.println(stu.getName());
}
3. 不再煩擾匿名類——自定義對(duì)象排序
兩個(gè)對(duì)象之間進(jìn)行屬性數(shù)值比較的話,在以前往往需要寫一個(gè)匿名比較器類Comparator,并在實(shí)現(xiàn)compare()方法,在方法中進(jìn)行比較并返回比較值;同樣地Stream結(jié)合Lambda,可以將匿名類的實(shí)現(xiàn)編寫為如下:
(s1,s2) -> s1.getAge() – s2.getAge() 等價(jià)于實(shí)現(xiàn)了Comparator里面的compare()方法;如下:
public int compare(Student s1, Student s2){
return s1.getAge() – s2.getAge();
}
4. 集合數(shù)據(jù)條件過濾
集合數(shù)據(jù)中如果需要根據(jù)一定的條件篩選過濾內(nèi)容,典型做法是逐個(gè)遍歷集
合對(duì)象,然后再將符合條件的對(duì)象設(shè)置到新的集合中;代碼量多且多余。Stream的filter()方法接受符合特定條件(Predicate)的對(duì)象的過濾。官方對(duì)這個(gè)方法的描述為:
大致意思是:在原有的stream中找到符合匹配查詢條件的元素并返回一個(gè)新stream。有了這個(gè)過濾方法,可以對(duì)集合中的數(shù)據(jù)進(jìn)行任意條件的過濾。
filter(student -> "女".equals(student.getGender())) 過濾保留集合中的學(xué)生對(duì)象的性別為 女 的學(xué)生對(duì)象。
5. 集合數(shù)據(jù)統(tǒng)計(jì)
mapToInt()可以將原Stream中的每個(gè)對(duì)象進(jìn)行操作并返回一個(gè)
整型數(shù)值重新生成一個(gè)
IntStream,然后再調(diào)用IntSteam中的計(jì)算平均值的方法average()。
需要注意的是,上述代碼中使用了 方法引用;maptToInt(Student::getAge)
等價(jià)于 maptToInt(stu -> stu.getAge())
計(jì)算學(xué)生總年齡上使用了parallelStream() ,該方法返回的Stream被稱為并行Stream,即可以并行地執(zhí)行這個(gè)Stream將要執(zhí)行的操作以此提高效率。需要注意的是雖然所有可以返回Stream的對(duì)象都可以返回并行Stream,不過并行Stream并非就是最好的,它總是犧牲其它不相關(guān)代碼的執(zhí)行效率;需要慎重使用。
6. 數(shù)據(jù)匹配
在數(shù)據(jù)集合中查找是否具有某特征的數(shù)據(jù),在Stream中是極其容易的;調(diào)用match相關(guān)的方法可以返回你想要的邏輯相關(guān)數(shù)據(jù)條件組合結(jié)果。
anyMatch(Student::isGzStudent) 的執(zhí)行操作為:在學(xué)生對(duì)象集合中查詢學(xué)生的姓名中是否包含有廣州兩個(gè)字,有則返回true否則返回false。
7. 數(shù)據(jù)歸約
在集合中如需要按照一定規(guī)則對(duì)所有數(shù)據(jù)進(jìn)行遞減式的操作;那么Stream的reduce()方法是個(gè)好選擇。reduce()在執(zhí)行規(guī)則的過程時(shí)記錄結(jié)果并將結(jié)果與集合剩余元素按照規(guī)則進(jìn)行處理。處理完后返回一個(gè)
Optional類型的容器對(duì)象。
上述代碼主要目的是將原是Student對(duì)象的Stream轉(zhuǎn)為一個(gè)以Student的姓名為對(duì)象的新Stream,然后再調(diào)用reduce((str1, str2) -> { return str1 + "," + str2;})將新Stream中的所有元素字符拼接起來。
從上述的Stream簡(jiǎn)單應(yīng)用中可發(fā)現(xiàn),在常用的集合數(shù)據(jù)或大批量數(shù)據(jù)的操作時(shí)方法容易,代碼簡(jiǎn)潔;這樣引入bug的機(jī)會(huì)就少,當(dāng)bug出現(xiàn)時(shí),代碼量少也更容易排查。