SpringBoot系列(七):Java8的Stream API,讓集合操作更為高效
作者:
修羅debug
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 by-sa 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。
摘要:本文我們將開個(gè)小插曲,分享介紹如何基于Java8提供的Stream特性,高效操作我們的集合,如List、Set、Map等等。其中,將主要介紹Stream特性提供的篩選過濾功能Filter、對象轉(zhuǎn)化功能Map、去重Distinct、排序Sorted、最小值Min以及最大值Max等核心操作。
內(nèi)容:了解過jdk動態(tài)的小伙伴們估計(jì)都知道,jdk已經(jīng)出到12的版本了(是不是有點(diǎn)懷疑目前仍然還在使用jdk1.6、jdk1.5的自己),當(dāng)然啦,我們都知道,不管jdk怎么升級,其底層核心數(shù)據(jù)庫以及jvm的特性是變化不大的,特別是java8之后的版本,其主要還是以java8作為奠基進(jìn)行一步步擴(kuò)張的。如下圖所示:
Java8對外提供的特性有很多,Stream便是其中的一大功能特性,除此之外,Java8還提供了接口默認(rèn)方法、Lambda表達(dá)式、函數(shù)式接口等等。說起Java8的Stream,其在集合的操作中著實(shí)發(fā)揮了強(qiáng)大的作用,別的不說,其中的簡潔、高效就特別令人眼前一亮。
Stream作為Java8的新特性,主要是基于lambda表達(dá)式,是對集合對象功能的增強(qiáng),它專注于對集合對象進(jìn)行各種高效、便利的聚合操作或者大批量的數(shù)據(jù)操作,可以提高編程的效率和代碼可讀性。
對于Stream的原理,正是其字面上的含義,Stream是將等待被處理的元素看做一種流,而流在管道中傳輸,并且可以在管道的節(jié)點(diǎn)上執(zhí)行相應(yīng)的處理操作,包括過濾篩選、去重、排序、聚合等等,元素流在管道中經(jīng)過中間操作的處理,最后由最終操作如collect等方法得到前面處理的結(jié)果。
對于Stream的原理,各位小伙伴也可以網(wǎng)上搜索相應(yīng)的文章進(jìn)行深入的研究,在這里我們只做簡單性的介紹。在后續(xù)的代碼實(shí)戰(zhàn)中,各位小伙伴可以看到上述對于Stream的原理將很好地在代碼中得到體現(xiàn)。
值得一提的是,Stream API在執(zhí)行的具體操作之前,需要先生成“流”,主要有兩種方式:
(1)stream() ? 為集合創(chuàng)建串行流
(2)parallelStream() - 為集合創(chuàng)建并行流
下面,我們在com.debug.springboot.server.dto目錄下新建一個(gè)實(shí)體類PersonDto,用于后續(xù)的Stream代碼實(shí)戰(zhàn)中,其源代碼如下所示:
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class PersonDto implements Serializable{
private Integer id;
private Integer age;
private String name;
}
其中,我們需要在該實(shí)體類中加入Equals()和HashCode()方法,用于后續(xù)執(zhí)行Stream的去重API做鋪墊!
另外,也需要在com.debug.springboot.server.utils 包目錄下新建一個(gè)Java8Util類,用于介紹一些比較突出的Steam API操作。
首先,我們需要在Java8Util類中新建一個(gè)可以被共用的對象集合,并采用static靜態(tài)代碼塊進(jìn)行初始化,如下所示:
private static List<PersonDto> list;
//初始化對象集合
static {
list= Lists.newLinkedList();
PersonDto dto1=new PersonDto(1,21,"趙六");
PersonDto dto2=new PersonDto(2,20,"張三");
PersonDto dto3=new PersonDto(3,22,"李四");
PersonDto dto4=new PersonDto(4,20,"王五");
list.add(dto1);
list.add(dto2);
list.add(dto3);
list.add(dto4);
}
1、首先介紹Stream的篩選過濾功能Filter:下面的代碼用于篩選過濾出年齡>20的對象集合,篩選過濾出名字為 王五 的對象信息,如下所示:
//篩選功能:filter 結(jié)合 collect方法 使用
public static void method1(){
//篩選年齡大于20的小伙子
if (list!=null && !list.isEmpty()){
System.out.println("\n--filter篩選功能1,結(jié)果:");
List<PersonDto> resList=list.stream().filter(p -> p.getAge() > 20).collect(Collectors.toList());
System.out.println(resList);
}
//篩選名字為 王五 的小伙子
if (list!=null && !list.isEmpty()){
System.out.println("\n--filter篩選功能2,結(jié)果:");
List<PersonDto> resList=list.stream().filter(p -> "王五".equals(p.getName())).collect(Collectors.toList());
System.out.println(resList);
}
}
之后,我們可以寫一個(gè)main方法,直接調(diào)用method1(),其運(yùn)行結(jié)果如下圖所示:
2、緊接著,我們介紹Stream中的轉(zhuǎn)換功能Map,如有以下的業(yè)務(wù)場景:當(dāng)?shù)玫揭粋€(gè)對象集合之后,要求我們需要抽取出每個(gè)對象中的某個(gè)字段,組成新的列表,比如抽取這個(gè)對象集合中的Id,或者Name,從而組成新的一個(gè)列表,源代碼如下所示:
//轉(zhuǎn)換map~按照指定的字段/元素屬性進(jìn)行轉(zhuǎn)換:結(jié)合 collect 方法使用
public static void method2(){
if (list!=null && !list.isEmpty()){
System.out.println("\n--轉(zhuǎn)換map~按照指定的字段/元素屬性進(jìn)行轉(zhuǎn)換,結(jié)果:");
Set<String> nameSet=list.stream().map(PersonDto::getName).collect(Collectors.toSet());
System.out.println(nameSet);
}
}
運(yùn)行結(jié)果如下圖所示:
3、然后,我們介紹一下“對象去重Distinct”的操作,這一操作,我們需要在實(shí)體類中加入 @EqualsAndHashCode 的lombok注解,源代碼如下所示:
//去重distinct ~ 配合對象的 equals() 和 hashCode()方法
public static void method3(){
//TODO:對象去重 - 對象需要實(shí)現(xiàn)equals hashCode方法
list.add(new PersonDto(4,20,"王五"));
System.out.println("去重以前:"+list);
List<PersonDto> resList=list.stream().distinct().collect(Collectors.toList());
System.out.println("去重以后:"+resList);
}
運(yùn)行結(jié)果如下圖所示:
4、順道,我們介紹一下Stream提供的Sorted排序功能,即我們可以根據(jù)對象中的某個(gè)字段實(shí)施某種排序方式(正序、倒序)等,如下代碼所示,我們可以借助Comparator比較器連續(xù)先后的對對象集合進(jìn)行排序:先按照年齡排序、再按照id排序(正序、倒序),源代碼如下所示:
//排序sorted
public static void method4(){
//按照年齡排序、再按照id排序
list.add(new PersonDto(0,20,"鄭六"));
if (list!=null && !list.isEmpty()){
List<PersonDto> resList=list.stream()
.sorted(Comparator.comparingInt(PersonDto::getAge).thenComparing(Comparator.comparing(PersonDto::getId)))
.collect(Collectors.toList());
System.out.println("按照年齡排序、再按照id排序-順序:\n"+resList);
}
//按照年齡排序、再按照id排序 ~ 也可以將最終結(jié)果倒序來
if (list!=null && !list.isEmpty()){
List<PersonDto> resList=list.stream()
.sorted(Comparator.comparingInt(PersonDto::getAge).thenComparing(Comparator.comparing(PersonDto::getId)).reversed())
.collect(Collectors.toList());
System.out.println("\n按照年齡排序、再按照id排序-最終倒序:\n"+resList);
}
}
其運(yùn)行結(jié)果如下圖所示:
5、最后,我們介紹一下如果根據(jù)對象中某個(gè)字段的最大、最小,從而取出整個(gè)對象集合中該最小值字段、最大值字段對應(yīng)的對象記錄,源碼如下所示:
//最小min、最大max
public static void method5(){
if (list!=null && !list.isEmpty()){
PersonDto p=list.stream()
.min(Comparator.comparingInt(PersonDto::getId))
.get();
System.out.println("\nid最?。?+p);
}
if (list!=null && !list.isEmpty()){
PersonDto p=list.stream()
.max(Comparator.comparingInt(PersonDto::getAge))
.get();
System.out.println("\n年齡最大:"+p);
}
}
其運(yùn)行結(jié)果如下圖所示:
補(bǔ)充:
1、本文涉及到的相關(guān)的源代碼可以到此地址,check出來進(jìn)行查看學(xué)習(xí):
https://gitee.com/steadyjack/SpringBootTechnology
2、目前Debug已將本文所涉及的內(nèi)容整理錄制成視頻教程,感興趣的小伙伴可以前往觀看學(xué)習(xí):
https://www.fightjava.com/web/index/course/detail/5
3、關(guān)注一下Debug的技術(shù)微信公眾號唄,最新的技術(shù)文章、技術(shù)課程以及技術(shù)專欄將會第一時(shí)間在公眾號發(fā)布哦!