自定义比较器排序及lambda和Stream的应用

一、用sort实现自定义排序

排序功能的核心方法:sort

sort可以对数组进行排序,被排序数组是sort方法的参数(之一)。

sort有很多重写方法,比较常见的是三个参数,sort(数组,起始,结束),用于把从起始到结束的数组元素排序,且数值只能按照大小顺序排序,字符串只能按照字典顺序排序。

想要自定义排序方法必须要用到sort(数组,排序接口),自定义排序规则的主要工作是定义排序接口

定义排序接口

interface Comparator<Object>

不能把接口直接放到参数上,所谓排序接口是实现了Comparator接口的类。

实现排序接口的类必须单独放在一个文件中,若和主方法在同一文件则会报错

Class 'XXX' is public, should be declared in a file named 'XXX.java'

 根据sort方法的使用逻辑,排序接口应返回正整数、负整数或零来代表排序规则,sort会自动按照返回的正负零来排序,如图是升序排序的写法:

public class NoComparator implements Comparator<Student> {
    public int compare(Student o1, Student o2) {
        return o2.getNo()-o1.getNo();
    }
}

前一个元素 减 后一个元素 会让数组升序排序。

当o1的No值大于o2时返回一个正数,相等时返回零,o1的No值小于o2时则返回负数,sort拿到这个返回值后操作的具体步骤可以忽略。

自定义排序接口的使用

创建有三个属性的Student类,用刚才定义的NoComparator作为sort的第二个参数,即可以编号(No)为依据进行排序。

在输出时分别获取Student类的三个属性并组成用逗号隔开的格式来打印。

public class Info {
    public static void main(String[] args) {
        Student sc = new Student(10000, "lzzzz", 19);
        Student sc1 = new Student(10001, "wxx", 20);
        Student sc2 = new Student(10003, "zh", 17);
        Student sc3 = new Student(10002, "g", 15);
        Student scs[] = new Student[]{sc,sc1,sc2,sc3};

        Arrays.sort(scs, new NoComparator());//这里传入了一个比较器
        for (int i = 0; i < scs.length; i++) {//用循环来输出数组
            System.out.println(scs[i].no+","+scs[i].name+","+scs[i].age);
        }

输出结果是: 

10002,g,15
10003,zh,17
10001,wxx,20
10000,lzzzz,19 


 

二、本例的一些简化的写法

使用匿名内部类定义比较器

匿名内部类指在其他方法内定义,只实现方法,无需命名的类或方法。用于定义临时使用的方法,无需再使用一个单独的文件来储存自定义比较器。

针对本例所描述的排序也可以用匿名内部类来定义排序规则。

Arrays.sort(scs, new Comparator<Student>(){
            public int compare(Student s1, Student s2){
                return s1.name.length() - s2.name.length();
            }
       });//内部类比较器

for (int i = 0; i < scs.length; i++) {//用循环来输出数组
            System.out.println(scs[i].no+","+scs[i].name+","+scs[i].age);
}

这里在参数中定义了排序规则。如下,对比较器的定义直接充当第二个参数,当场定义如何排序。

new Comparator<Student>(){
            public int compare(Student s1, Student s2){
                return s1.name.length() - s2.name.length();
            }
}

因为这个参数一定要实现Comparator接口,所以在创建(new)的时候先写Comparator<T>,再写方法的实现过程,相当于先定义了一个实现Comparator的类,再实现,不过这个类因为没有命名所以只能在本次sort中使用。由于这次定义的排序规则是name属性的长度,所以输出结果是:

10003,zh,17
10002,g,15
10001,wxx,20
10000,lzzzz,19

使用lambda表达式来简化匿名内部类的定义

lambda表达式可以极大地简化匿名内部类的定义,只需注明接收两个Student类对象,返回age的大小关系即可使sort根据age的大小来排序。

Arrays.sort(scs, (Student s1, Student s2) -> s1.age - s2.age);//这里使用lambda表达式代替比较器

for (int i = 0; i < scs.length; i++) {//用循环来输出数组
            System.out.println(scs[i].no+","+scs[i].name+","+scs[i].age);
}

输出结果是

10002,g,15
10003,zh,17
10000,lzzzz,19
10001,wxx,20

 除了排序规则可以简化以外,遍历输出也可以简化。

用forEach方法遍历数组,再用双冒号表达式表明使用输出语句。

Arrays.sort(scs, (Student s1, Student s2) -> s1.age - s2.age);//这里使用lambda表达式代替比较器

Arrays.stream(scs).forEach(System.out::println);//用双冒号表达式输出

 其中,forEach是列表类的方法,作用是遍历列表元素,送入括号内让括号内指定的方法来操作遍历的元素。与往常的方法不同,操作对象在外,执行方法在内。

System.out::println的意思是执行System.out包中的println方法,表明来源包和方法名就行了,连括号都省了。

Arrays.stream(数组)可以把数组转化成流,相当于列表,这样就可以用forEach来遍历了。

结果是:

Student@74a14482
Student@4554617c
Student@677327b6
Student@1540e19d

 普通的for循环+println可以带参数,所以能够指定对象的某属性进行操作,如s1.no表示s1的号码,才能分别输出对象的各个属性。若直接输出对象则只能得到首地址。

forEach中的打印方法无法携带参数,只能直接打印对象,所以只得到了首地址。但是想要通过forEach来遍历数组则只能直接打印对象。

如何实现直接打印对象?

其实在打印方法中都会调用toString方法,对于基本数据类型,toString将值变为字符串,然后再被打印出来。对于实例对象,变量本身存储的就是对象的首地址,toString只是先让这个地址变成字符串然后输出而已。所以可以通过重写toString方法来实现复杂对象的打印。

public String toString(){
        return getNo()+","+this.name+","+this.age;
    }

在需要打印的类中重写toString方法,本例中在Student类中添加toString,直接打印对象时即可按照返回值指定的格式输出结果。

10002,g,15
10003,zh,17
10000,lzzzz,19
10001,wxx,20

其中,虽然no和age都是整数型,但是name和逗号是字符串,与字符串相加即可变成一个长的整个字符串。

同理,如果在输出对象时只想输出age,可以把toString方法作如下重写。

public String toString(){
        return this.age+"";
    }

空字符串也是字符串,也能把返回结果变成字符串值并用打印方法输出。能得到四个年龄值。

15
17
19
20


 三、集合的排序

Arrays.sort()只能排序数组,想要排序集合只能使用Collections.sort()。原理与数组相同。

先定义比较器

public class NumComparator implements Comparator<item> {
    public int compare(item o1, item o2){
        return o1.num - o2.num;
    }
}

新的类是item,所以尖括号里写item,除此之外和上例完全一致。

public static void main(String[] args) {
        List<item> items = new ArrayList<>();
        items.add(new item(1, "zhangsan", 15));
        items.add(new item(2,"lisi", 19));
        items.add(new item(3, "wangwu", 16));//创建集合

        items.forEach(System.out::println);//先打印一遍看看重写toString的效果

    //第一遍根据年龄排序
        Collections.sort(items, (item o1, item o2) -> o1.age - o2.age);
        items.forEach(System.out::println);

    //第二遍根据编号排序,用比较器
        Collections.sort(items, new NumComparator());
        items.forEach(System.out::println);
    
    //第三遍根据名称长度排序,用匿名内部类
        Collections.sort(items, new Comparator<item>() {
            @Override
            public int compare(item o1, item o2) {
                return o1.name.length() - o2.name.length();
            }
        });
        items.forEach(System.out::println);
    }

这是新的toString。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
自定义排序可以通过使用Stream的sorted()方法结合Comparator来实现。首先,你可以创建一个实现Comparator接口的匿名类或者使用Lambda表达式来定义自定义排序规则。然后,将这个Comparator作为参数传递给sorted()方法。这样,流中的元素就会按照你指定的自定义排序规则进行排序。 例如,如果你有一个包含Person对象的流,你可以按照他们的年龄进行排序,可以使用如下的代码: stream.sorted((p1, p2) -> p1.getAge() - p2.getAge()) 在上述代码中,我们使用Lambda表达式创建了一个Comparator实例,比较了两个Person对象的年龄。通过调用sorted()方法并传递这个Comparator实例,流中的Person对象将按照年龄进行排序。 另外,你也可以使用Comparator的reversed()方法来实现反向排序。例如,如果你想按照年龄降序排序,可以使用如下的代码: stream.sorted((p1, p2) -> p2.getAge() - p1.getAge()) 在这个例子中,我们交换了比较的顺序,使得年龄较大的Person对象排在前面,实现了按照年龄降序排序。 总的来说,使用sorted()方法和Comparator可以实现流的自定义排序。你可以根据具体的需求定义Comparator来指定排序规则,然后将其传递给sorted()方法即可。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Java8 stream 排序以及自定义比较器,很实用](https://blog.csdn.net/xiaohao718/article/details/125379045)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [Java8 stream 多条件排序,且支持自定义排序](https://blog.csdn.net/weixin_41753664/article/details/123198505)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值