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