引入
用了一段时间的jdk8的新特性,lambda表达式、方法引用、stream流,用起来是真的顺手啊,最近碰到了一个排序的问题,引发了一些思考,然后就写了这篇博客,归纳总结sorted的用法,在做笔记的同时也让自己有更深的理解。
数据准备
1. 依赖
我喜欢用google的集合工具类,让我构造测试数据更简便。
然后也用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. 相关类
user类
/* *链式调用,我也有写相关博客,不过不是介绍基础用法的,是这个链式调用一个不太完美的地方, *感兴趣的可以去看一看: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类,注意:这个类在后续的测试中要改动
@data @accessors(chain = true) public class address { private string province; private string city; private string county; }
数据准备,
private list<user> users = new arraylist<user>() {{ add(new user().setusername("张三").setpassword("123456").setage(20).setheight(170) .setaddress(new address().setprovince("四川省").setcity("成都市").setcounty("武侯区")) .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("锦江区")) .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("青羊区")) .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("高新区")) .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); }};
初体验
stream流、方法引用、lambda那些前置知识咱们就不说了哈,直接上手,先体直观的验一下排序
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()); // 等效写法如下 // 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=锦江区), others={sorted=ddd, eee=eee, fff=fff}), user(username=赵六, password=123456, age=17, height=168, address=address(province=四川省, city=成都市, county=高新区), others={sorted=jjj, kkk=kkk, lll=lll}), user(username=张三, password=123456, age=20, height=170, address=address(province=四川省, city=成都市, county=武侯区), others={sorted=aaa, bbb=bbb, ccc=ccc}), user(username=王五, password=123456, age=20, height=180, address=address(province=四川省, city=成都市, county=青羊区), others={sorted=ggg, hhh=hhh, iii=iii})] system.out.println(sortedusers);
很简单,也没啥难理解的,就是排序
基础用法
排序的初体验之后,我们来看看几种正式场景下的使用
1. 降序几种方式
在上面的体验排序中,排序的结果默认都是升序的,那如果我要降序呢?那怎么办?有三种方式,或者三种写法
1. 使用reversed
根据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=武侯区), others={sorted=aaa, bbb=bbb, ccc=ccc}), user(username=王五, password=123456, age=20, height=180, address=address(province=四川省, city=成都市, county=青羊区), others={sorted=ggg, hhh=hhh, iii=iii}), user(username=赵六, password=123456, age=17, height=168, address=address(province=四川省, city=成都市, county=高新区), others={sorted=jjj, kkk=kkk, lll=lll}), user(username=李四, password=123456, age=16, height=175, address=address(province=四川省, city=成都市, county=锦江区), others={sorted=ddd, eee=eee, fff=fff})] system.out.println(collect);
根据user中age先排序,默认情况下是升序,然后再逆序一下,就变成了降序。但是这样有点不好,因为是先排序然后在逆序的,要两步操作。
2.使用comparator.reverseorder
根据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=武侯区), others={sorted=aaa, bbb=bbb, ccc=ccc}), user(username=王五, password=123456, age=20, height=180, address=address(province=四川省, city=成都市, county=青羊区), others={sorted=ggg, hhh=hhh, iii=iii}), user(username=赵六, password=123456, age=17, height=168, address=address(province=四川省, city=成都市, county=高新区), others={sorted=jjj, kkk=kkk, lll=lll}), user(username=李四, password=123456, age=16, height=175, address=address(province=四川省, city=成都市, county=锦江区), others={sorted=ddd, eee=eee, fff=fff})] system.out.println(collect1);
这种方式原理和和方法1差不多,只是写法不一样
3. 在sorted中使用compareto
方式1和方式2都是利用sorted默认序,然后再逆序来实现排序的,这样会有两个步骤,先升序,然后再逆序。难道就没有直接按照降序来排序的方法?肯定是有的。既然是排序,肯定是可以指定规则的
- 按照年龄降序
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=武侯区), others={sorted=aaa, bbb=bbb, ccc=ccc}), user(username=王五, password=123456, age=20, height=180, address=address(province=四川省, city=成都市, county=青羊区), others={sorted=ggg, hhh=hhh, iii=iii}), user(username=赵六, password=123456, age=17, height=168, address=address(province=四川省, city=成都市, county=高新区), others={sorted=jjj, kkk=kkk, lll=lll}), user(username=李四, password=123456, age=16, height=175, address=address(province=四川省, city=成都市, county=锦江区), others={sorted=ddd, eee=eee, fff=fff})] system.out.println(collect2);
这种方式是通过integer中的compareto方法来实现降序的,当然也可以自己实现,只不过age是integer类型,既然integer中已经实现两个integer比较的方法,就可以偷个懒
4. 在sorted自定义规则
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=武侯区), others={sorted=aaa, bbb=bbb, ccc=ccc}), user(username=王五, password=123456, age=20, height=180, address=address(province=四川省, city=成都市, county=青羊区), others={sorted=ggg, hhh=hhh, iii=iii}), user(username=赵六, password=123456, age=17, height=168, address=address(province=四川省, city=成都市, county=高新区), others={sorted=jjj, kkk=kkk, lll=lll}), user(username=李四, password=123456, age=16, height=175, address=address(province=四川省, city=成都市, county=锦江区), others={sorted=ddd, eee=eee, fff=fff})] system.out.println(collect3);
比较实际的最终结果就是返回一个数字,大于0、小于0、等于0,分别就代表大于、小于、等于,所以在sorted方法中返回一个做减法的数字即可。
2. 多级排序
比如我现在的需求是先按照年龄降序,年龄相同的再按照名字降序
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=青羊区), others={sorted=ggg, hhh=hhh, iii=iii}), user(username=张三, password=123456, age=20, height=170, address=address(province=四川省, city=成都市, county=武侯区), others={sorted=aaa, bbb=bbb, ccc=ccc}), user(username=赵六, password=123456, age=17, height=168, address=address(province=四川省, city=成都市, county=高新区), others={sorted=jjj, kkk=kkk, lll=lll}), user(username=李四, password=123456, age=16, height=175, address=address(province=四川省, city=成都市, county=锦江区), others={sorted=ddd, eee=eee, fff=fff})] system.out.println(collect);
thencomparing顾名思义,再比较
进阶
放在以前,我肯定会从stream 排序的api说起,看看有哪些方法,再看怎么调用,但是这流式太过抽象,所以我先讲了怎么用,再回头来看看有哪些api,本质是什么
1. 本质
在java.util.stream.stream中,sorted方法有两个重载形式
一个是无参,一个是需要一个参数java.util.comparator。
1. stream sorted();
其实这两个方法我们都用过,在初体验中,第一个就是无参的,这样会根据默认规则排序,至于默认规则是什么,就是排序对象实现的java.lang.comparable接口中的compareto方法,不然你试试跑一下这个
list<user> collect = users.stream().sorted().collect(collectors.tolist());
直接报错,报错的原因就是,你要排序一堆user,但是sorted这个无参的方法不知道排序的规则是什么。所以,在使用这个无参的方法时,被排序的元素必须得实现java.lang.comparable接口,来指定排序规则。
2. stream sorted(comparator<? super t> comparator);
除了初体验中的第一个排序,其他的全都是使用的这个方法,很神奇是吧?我好像传的参数不止这样。
但事实上就是这样子,只传了这个一个参数,无非有两种传参形式:一种是确确实实的传了一个java.util.comparator进去,另外一种是自己实现了java.util.comparator中的抽象方法compare,这个方法用来进行元素间的比较。因为java.util.comparator是一个函数式接口,接口中只有compare这一个抽象方法,所以可以结合lambda表达式使用。
我们使用的.sorted(comparator.comparing(……))其实就是直接传了一个comparator进去,因为comparator.comparing这个方法:java.util.comparator#comparing(java.util.function.function<? super t,? extends u>, java.util.comparator<? super u>) 返回的就是一个comparator。
而类似这种使用:sorted((x, y) -> y.getage().compareto(x.getage()))或者sorted((x, y) -> y.getage() - x.getage()),其实就是我们自己在实现java.util.comparator中的抽象方法compare,这其实就是匿名内部类---->简化---->lambda表达式的这么一个过程,其实还可以简化成 方法引用。
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());
为什么第二个排序会有问题?
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论