美文网首页
java stream使用指南-------sorted使用及进

java stream使用指南-------sorted使用及进

作者: 分布式与微服务 | 来源:发表于2022-10-14 09:08 被阅读0次

    引入

    用了一段时间的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());
    

    为什么第二个排序会有问题?

    相关文章

      网友评论

          本文标题:java stream使用指南-------sorted使用及进

          本文链接:https://www.haomeiwen.com/subject/kcnmwrtx.html