java?stream使用指南之sorted使用及進(jìn)階方式
引入
用了一段時(shí)間的jdk8的新特性,lambda表達(dá)式、方法引用、stream流,用起來(lái)是真的順手啊,最近碰到了一個(gè)排序的問(wèn)題,引發(fā)了一些思考,然后就寫(xiě)了這篇博客,歸納總結(jié)sorted的用法,在做筆記的同時(shí)也讓自己有更深的理解。
數(shù)據(jù)準(zhǔn)備
1. 依賴
我喜歡用google的集合工具類,讓我構(gòu)造測(cè)試數(shù)據(jù)更簡(jiǎn)便。
然后也用lombok,依賴:
<!--google集合工具類--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.0-jre</version> </dependency> <!--lombok,需要插件配合使用--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency>
2. 相關(guān)類
User類
/* *鏈?zhǔn)秸{(diào)用,我也有寫(xiě)相關(guān)博客,不過(guò)不是介紹基礎(chǔ)用法的,是這個(gè)鏈?zhǔn)秸{(diào)用一個(gè)不太完美的地方, *感興趣的可以去看一看:https://blog.csdn.net/ql_7256/article/details/120274432 */ @Data @Accessors(chain = true) public class User { private String username; private String password; private Integer age; private Integer height; private Address address; private Map others; }
Address類,注意:這個(gè)類在后續(xù)的測(cè)試中要改動(dòng)
@Data @Accessors(chain = true) public class Address { private String province; private String city; private String county; }
數(shù)據(jù)準(zhǔn)備,
private List<User> users = new ArrayList<User>() {{ add(new User().setUsername("張三").setPassword("123456").setAge(20).setHeight(170) .setAddress(new Address().setProvince("四川省").setCity("成都市").setCounty("武侯區(qū)")) .setOthers(ImmutableMap.builder().put("sorted","AAA").put("bbb","BBB").put("ccc","CCC").build())); add(new User().setUsername("李四").setPassword("123456").setAge(16).setHeight(175) .setAddress(new Address().setProvince("四川省").setCity("成都市").setCounty("錦江區(qū)")) .setOthers(ImmutableMap.builder().put("sorted","DDD").put("eee","EEE").put("fff","FFF").build())); add(new User().setUsername("王五").setPassword("123456").setAge(20).setHeight(180) .setAddress(new Address().setProvince("四川省").setCity("成都市").setCounty("青羊區(qū)")) .setOthers(ImmutableMap.builder().put("sorted","GGG").put("hhh","HHH").put("iii","III").build())); add(new User().setUsername("趙六").setPassword("123456").setAge(17).setHeight(168) .setAddress(new Address().setProvince("四川省").setCity("成都市").setCounty("高新區(qū)")) .setOthers(ImmutableMap.builder().put("sorted","JJJ").put("kkk","KKK").put("lll","LLL").build())); }}; private List<String> strings = new ArrayList<String>() {{ add("222");add("666");add("444");add("111");add("333");add("555"); }}; private List<Integer> integers = new ArrayList<Integer>() {{ add(222);add(555);add(666);;add(333);add(444);add(111); }}; private List others = new ArrayList() {{ add(444);add(555);add(666);add(111);add(222);add(333); }};
初體驗(yàn)
stream流、方法引用、lambda那些前置知識(shí)咱們就不說(shuō)了哈,直接上手,先體直觀的驗(yàn)一下排序
List<String> sortedStrings = strings.stream().sorted().collect(Collectors.toList()); // [111, 222, 333, 444, 555, 666] System.out.println(sortedStrings); List<User> sortedUsers = users.stream().sorted(Comparator.comparing(e -> e.getAge())).collect(Collectors.toList()); // 等效寫(xiě)法如下 // List<User> sortedUsers = users.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList()); // [User(username=李四, password=123456, age=16, height=175, address=Address(province=四川省, city=成都市, county=錦江區(qū)), others={sorted=DDD, eee=EEE, fff=FFF}), User(username=趙六, password=123456, age=17, height=168, address=Address(province=四川省, city=成都市, county=高新區(qū)), others={sorted=JJJ, kkk=KKK, lll=LLL}), User(username=張三, password=123456, age=20, height=170, address=Address(province=四川省, city=成都市, county=武侯區(qū)), others={sorted=AAA, bbb=BBB, ccc=CCC}), User(username=王五, password=123456, age=20, height=180, address=Address(province=四川省, city=成都市, county=青羊區(qū)), others={sorted=GGG, hhh=HHH, iii=III})] System.out.println(sortedUsers);
很簡(jiǎn)單,也沒(méi)啥難理解的,就是排序
基礎(chǔ)用法
排序的初體驗(yàn)之后,我們來(lái)看看幾種正式場(chǎng)景下的使用
1. 降序幾種方式
在上面的體驗(yàn)排序中,排序的結(jié)果默認(rèn)都是升序的,那如果我要降序呢?那怎么辦?有三種方式,或者三種寫(xiě)法
1. 使用reversed
根據(jù)user中的age降序
List<User> collect = users.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(Collectors.toList()); // [User(username=張三, password=123456, age=20, height=170, address=Address(province=四川省, city=成都市, county=武侯區(qū)), others={sorted=AAA, bbb=BBB, ccc=CCC}), User(username=王五, password=123456, age=20, height=180, address=Address(province=四川省, city=成都市, county=青羊區(qū)), others={sorted=GGG, hhh=HHH, iii=III}), User(username=趙六, password=123456, age=17, height=168, address=Address(province=四川省, city=成都市, county=高新區(qū)), others={sorted=JJJ, kkk=KKK, lll=LLL}), User(username=李四, password=123456, age=16, height=175, address=Address(province=四川省, city=成都市, county=錦江區(qū)), others={sorted=DDD, eee=EEE, fff=FFF})] System.out.println(collect);
根據(jù)User中age先排序,默認(rèn)情況下是升序,然后再逆序一下,就變成了降序。但是這樣有點(diǎn)不好,因?yàn)槭窍扰判蛉缓笤谀嫘虻模獌刹讲僮鳌?/p>
2.使用Comparator.reverseOrder
根據(jù)user中的age降序
List<User> collect1 = users.stream().sorted(Comparator.comparing(User::getAge, Comparator.reverseOrder())).collect(Collectors.toList()); // [User(username=張三, password=123456, age=20, height=170, address=Address(province=四川省, city=成都市, county=武侯區(qū)), others={sorted=AAA, bbb=BBB, ccc=CCC}), User(username=王五, password=123456, age=20, height=180, address=Address(province=四川省, city=成都市, county=青羊區(qū)), others={sorted=GGG, hhh=HHH, iii=III}), User(username=趙六, password=123456, age=17, height=168, address=Address(province=四川省, city=成都市, county=高新區(qū)), others={sorted=JJJ, kkk=KKK, lll=LLL}), User(username=李四, password=123456, age=16, height=175, address=Address(province=四川省, city=成都市, county=錦江區(qū)), others={sorted=DDD, eee=EEE, fff=FFF})] System.out.println(collect1);
這種方式原理和和方法1差不多,只是寫(xiě)法不一樣
3. 在sorted中使用compareTo
方式1和方式2都是利用sorted默認(rèn)序,然后再逆序來(lái)實(shí)現(xiàn)排序的,這樣會(huì)有兩個(gè)步驟,先升序,然后再逆序。難道就沒(méi)有直接按照降序來(lái)排序的方法?肯定是有的。既然是排序,肯定是可以指定規(guī)則的
- 按照年齡降序
List<User> collect2 = users.stream().sorted((x, y) -> y.getAge().compareTo(x.getAge())).collect(Collectors.toList()); // [User(username=張三, password=123456, age=20, height=170, address=Address(province=四川省, city=成都市, county=武侯區(qū)), others={sorted=AAA, bbb=BBB, ccc=CCC}), User(username=王五, password=123456, age=20, height=180, address=Address(province=四川省, city=成都市, county=青羊區(qū)), others={sorted=GGG, hhh=HHH, iii=III}), User(username=趙六, password=123456, age=17, height=168, address=Address(province=四川省, city=成都市, county=高新區(qū)), others={sorted=JJJ, kkk=KKK, lll=LLL}), User(username=李四, password=123456, age=16, height=175, address=Address(province=四川省, city=成都市, county=錦江區(qū)), others={sorted=DDD, eee=EEE, fff=FFF})] System.out.println(collect2);
這種方式是通過(guò)Integer中的compareTo方法來(lái)實(shí)現(xiàn)降序的,當(dāng)然也可以自己實(shí)現(xiàn),只不過(guò)age是Integer類型,既然Integer中已經(jīng)實(shí)現(xiàn)兩個(gè)Integer比較的方法,就可以偷個(gè)懶
4. 在sorted自定義規(guī)則
List<User> collect3 = users.stream().sorted((x, y) -> y.getAge() - x.getAge()).collect(Collectors.toList()); // [User(username=張三, password=123456, age=20, height=170, address=Address(province=四川省, city=成都市, county=武侯區(qū)), others={sorted=AAA, bbb=BBB, ccc=CCC}), User(username=王五, password=123456, age=20, height=180, address=Address(province=四川省, city=成都市, county=青羊區(qū)), others={sorted=GGG, hhh=HHH, iii=III}), User(username=趙六, password=123456, age=17, height=168, address=Address(province=四川省, city=成都市, county=高新區(qū)), others={sorted=JJJ, kkk=KKK, lll=LLL}), User(username=李四, password=123456, age=16, height=175, address=Address(province=四川省, city=成都市, county=錦江區(qū)), others={sorted=DDD, eee=EEE, fff=FFF})] System.out.println(collect3);
比較實(shí)際的最終結(jié)果就是返回一個(gè)數(shù)字,大于0、小于0、等于0,分別就代表大于、小于、等于,所以在sorted方法中返回一個(gè)做減法的數(shù)字即可。
2. 多級(jí)排序
比如我現(xiàn)在的需求是先按照年齡降序,年齡相同的再按照名字降序
List<User> collect = users.stream().sorted(Comparator.comparing(User::getAge, Comparator.reverseOrder()).thenComparing(User::getHeight, Comparator.reverseOrder())).collect(Collectors.toList()); // [User(username=王五, password=123456, age=20, height=180, address=Address(province=四川省, city=成都市, county=青羊區(qū)), others={sorted=GGG, hhh=HHH, iii=III}), User(username=張三, password=123456, age=20, height=170, address=Address(province=四川省, city=成都市, county=武侯區(qū)), others={sorted=AAA, bbb=BBB, ccc=CCC}), User(username=趙六, password=123456, age=17, height=168, address=Address(province=四川省, city=成都市, county=高新區(qū)), others={sorted=JJJ, kkk=KKK, lll=LLL}), User(username=李四, password=123456, age=16, height=175, address=Address(province=四川省, city=成都市, county=錦江區(qū)), others={sorted=DDD, eee=EEE, fff=FFF})] System.out.println(collect);
thenComparing顧名思義,再比較
進(jìn)階
放在以前,我肯定會(huì)從stream 排序的api說(shuō)起,看看有哪些方法,再看怎么調(diào)用,但是這流式太過(guò)抽象,所以我先講了怎么用,再回頭來(lái)看看有哪些api,本質(zhì)是什么
1. 本質(zhì)
在java.util.stream.Stream中,sorted方法有兩個(gè)重載形式
一個(gè)是無(wú)參,一個(gè)是需要一個(gè)參數(shù)java.util.Comparator。
1. Stream sorted();
其實(shí)這兩個(gè)方法我們都用過(guò),在初體驗(yàn)中,第一個(gè)就是無(wú)參的,這樣會(huì)根據(jù)默認(rèn)規(guī)則排序,至于默認(rèn)規(guī)則是什么,就是排序?qū)ο髮?shí)現(xiàn)的java.lang.Comparable接口中的compareTo方法,不然你試試跑一下這個(gè)
List<User> collect = users.stream().sorted().collect(Collectors.toList());
直接報(bào)錯(cuò),報(bào)錯(cuò)的原因就是,你要排序一堆User,但是sorted這個(gè)無(wú)參的方法不知道排序的規(guī)則是什么。所以,在使用這個(gè)無(wú)參的方法時(shí),被排序的元素必須得實(shí)現(xiàn)java.lang.Comparable接口,來(lái)指定排序規(guī)則。
2. Stream sorted(Comparator<? super T> comparator);
除了初體驗(yàn)中的第一個(gè)排序,其他的全都是使用的這個(gè)方法,很神奇是吧?我好像傳的參數(shù)不止這樣。
但事實(shí)上就是這樣子,只傳了這個(gè)一個(gè)參數(shù),無(wú)非有兩種傳參形式:一種是確確實(shí)實(shí)的傳了一個(gè)java.util.Comparator進(jìn)去,另外一種是自己實(shí)現(xiàn)了java.util.Comparator中的抽象方法compare,這個(gè)方法用來(lái)進(jìn)行元素間的比較。因?yàn)閖ava.util.Comparator是一個(gè)函數(shù)式接口,接口中只有compare這一個(gè)抽象方法,所以可以結(jié)合lambda表達(dá)式使用。
我們使用的.sorted(Comparator.comparing(……))其實(shí)就是直接傳了一個(gè)Comparator進(jìn)去,因?yàn)镃omparator.comparing這個(gè)方法:java.util.Comparator#comparing(java.util.function.Function<? super T,? extends U>, java.util.Comparator<? super U>) 返回的就是一個(gè)Comparator。
而類似這種使用:sorted((x, y) -> y.getAge().compareTo(x.getAge()))或者sorted((x, y) -> y.getAge() - x.getAge()),其實(shí)就是我們自己在實(shí)現(xiàn)java.util.Comparator中的抽象方法compare,這其實(shí)就是匿名內(nèi)部類---->簡(jiǎn)化---->lambda表達(dá)式的這么一個(gè)過(guò)程,其實(shí)還可以簡(jiǎn)化成 方法引用。
2. 拓展及思考
List<User> sortedUsers = users.stream().sorted(Comparator.comparing(x -> x.getOthers().get("aaa").toString())).collect(Collectors.toList()); List<User> sortedUsers2 = users.stream().sorted(Comparator.comparing(x -> x.getOthers().get("aaa").toString()).reversed()).collect(Collectors.toList());
為什么第二個(gè)排序會(huì)有問(wèn)題?
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
自定義spring mvc的json視圖實(shí)現(xiàn)思路解析
這篇文章主要介紹了自定義spring mvc的json視圖的實(shí)現(xiàn)思路解析,本文給大家介紹的非常詳細(xì),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-12-12如何使用SpringBoot集成Kafka實(shí)現(xiàn)用戶數(shù)據(jù)變更后發(fā)送消息
Spring Boot集成Kafka實(shí)現(xiàn)用戶數(shù)據(jù)變更后,向其他廠商發(fā)送消息,我們需要考慮配置Kafka連接、創(chuàng)建Kafka Producer發(fā)送消息、監(jiān)聽(tīng)用戶數(shù)據(jù)變更事件,并將事件轉(zhuǎn)發(fā)到Kafka,本文分步驟給大家講解使用SpringBoot集成Kafka實(shí)現(xiàn)用戶數(shù)據(jù)變更后發(fā)送消息,感興趣的朋友一起看看吧2024-07-07基于Spring概念模型:PathMatcher 路徑匹配器
這篇文章主要介紹了Spring概念模型:PathMatcher 路徑匹配器,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09Java類和對(duì)象習(xí)題及詳細(xì)答案解析
這篇文章主要介紹了Java類和對(duì)象的相關(guān)知識(shí),包括局部變量初始化、靜態(tài)方法、靜態(tài)導(dǎo)入、構(gòu)造方法、代碼塊執(zhí)行順序、toString方法重寫(xiě)、類變量和靜態(tài)成員變量的訪問(wèn)等,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-02-02MyBatis實(shí)現(xiàn)簡(jiǎn)單的數(shù)據(jù)表分月存儲(chǔ)
本文主要介紹了MyBatis實(shí)現(xiàn)簡(jiǎn)單的數(shù)據(jù)表分月存儲(chǔ),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03SpringCloud整合分布式服務(wù)跟蹤zipkin的實(shí)現(xiàn)
這篇文章主要介紹了SpringCloud整合分布式服務(wù)跟蹤zipkin的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09