java比较器

Java比较器

  • 在java中经常会涉及到对象数组的排序问题,那么就涉及到对象之间的比较问题。

java对象中,正常情况下,只能进行比较: == 或 != 。不能使用 > 或 < 的,但是在开发场景中,我们需要对多个对象进行排序,就是需要比较对象的大小。

要如何实现呢?

这就需要使用到这两个接口的任何一个:ComparableComparator 【比较对象的大小需要用到这两个接口,下面会进行讲解】

测试:

public class CompareTest {
    public static void main(String[] args) {
      	//创建一个字符串数组
        String[] arr = new String[]{"g","h","a","l","y","b"};
      	//使用Arrays.sort对arr进行排序
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));
        //输出的结果为:[a, b, g, h, l, y] ,成功对字符进行排序

        //为什么可以对String 进行排序呢?
        //追加进入String 里面,可以看到String他实现了 Comparable接口
      
        //public final class String implements java.io.Serializable, Comparable<String>, CharSequence {  }
      
        //实现了 Comparable接口就需要去重写他的方法compareTo,在String 类中找到compareTo方法
        /**
         * 可以看到已经对compareTo进行了重写,他呢就是比较两个字符串之间的大小,方法里面就是比较的方式
         * public int compareTo(String anotherString) {
         *         int len1 = value.length;
         *         int len2 = anotherString.value.length;
         *         int lim = Math.min(len1, len2);
         *         char v1[] = value;
         *         char v2[] = anotherString.value;
         *
         *         int k = 0;
         *         while (k < lim) {
         *             char c1 = v1[k];
         *             char c2 = v2[k];
         *             if (c1 != c2) {
         *                 return c1 - c2;
         *             }
         *             k++;
         *         }
         *         return len1 - len2;
         *     }
         */
    }
}

自然排序

  • Comparable接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序。
  • 实现Comparable的类必须重写compareTo(Object obj) 方法,两个对象即通过compareTo(Object obj) 方法的返回值来比较大小。(如果当前对象this大于形参对象obj,则返回正整数,如果当前对象this小于形参对象obj,则返回负整数,如果当前对象this等于形参对象obj,则返回零。)
  • 实现Comparable接口的对象列表(和数组)可以通过Collections.sort 或 Arrays.sort 进行自动排序。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需制定比较器。
  • 对于类C的每一个e1 和 e2 来说当 e1.compareTo(e2)==0 与 e1.equals(e2)具有相同的boolean值时,类C的自然排序才叫做与equals一致。

Comparable 接口的使用说明

  • 像String 和其他包装类(Integer…)等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式。他们是进行了从小到大的排序。
  • 比较的方式是什么呢?这时就需要了解重写compareTo(obj)方法的规则:
    • 如果当前对象this大于形参对象obj,则返回正整数。
    • 如果当前对象this小于形参对象obj,则返回负整数。
    • 如果当前对象this等于形参对象obj,则返回零
  • 对于自定义类来说,如果需要排序,可以让自定义类实现Comparable接口,重写compareTo(obj) 方法。在compareTo(obj) 方法中指明如何排序

测试:

往数组中加入对象

1、首先创建一个 User 类,有两个属性 name、age,并且写好get、set、toString方法

public class User implements Comparable{
    private String name;
    private Integer age;
    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

2、创建一个测试类,往数组中添加对象

import java.util.Arrays;
public class TestDemo {
    public static void main(String[] args) {
        //创建User对象的数组
        User[] users = new User[4];
        //往数组里面存放数据,放的是对象
        users[0] = new User("xiaohong",19);
        users[1] = new User("liming",16);
        users[2] = new User("zhangsan",18);
        users[3] = new User("wangwu",22);
        // 通过Arrays.sort方法,对users数组里面的元素进行排序
        Arrays.sort(users);
        //输出排序后的数组
        System.out.println(Arrays.toString(users));
        /**
         * 结果:
         *      报了一个类转换异常 ClassCastException
         *      com.xun.pojo.User cannot be cast to java.lang.Comparable错误
         *       说是 user不能转换为java.lang.Comparable
         *
         *  为什么会跟到Comparable打交道呢?因为在调用Arrays.sort这个方法的时候,
         *  他涉及到跟user这个数组的元素进行排序,那就需要比较大小!而对象比较大小只能跟Comparable和Comparator打交道,
         *  默认的是跟Comparable打交道,所以它就提示类型转换异常
         */
    }
}

所以想对对象进行排序,就需要去实现Comparable接口,并且重写compareTo(obj) 方法指明如何排序

package com.xun.pojo;
//实现 Comparable接口
public class User implements Comparable{
    private String name;
    private Integer age;
    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    //重写compareTo方法,并且指明如何排序
    @Override
    public int compareTo(Object o) {
        //判断o对象是否是 User对象的实例,是就返回true
        if (o instanceof User){
            //由于需要向下转型,所以需要强转才能获取到User里面的属性
            User obj = (User) o;
        //方式一:
            //按照age进行排序
                //如果当前的age大于形参的age,就返回1
            if (this.age> obj.age){
                return 1;
                //如果当前的age小于形参的age,就返回-1
            }else if(this.age < obj.age){
                return -1;
                //否则就是相等,就返回0
            }else{
                return 0;
            }
        //方式二:
            //使用下面这种也行,包装类里面实现了Comparable接口,并且重写了compareTo方法,指明了如何排序
            //可以追加进去看看,发现也跟方式一差不多,传递两个参数,让着两个参数进行比较,然后根据判断的结果返回 -1、0、1
            //return Double.compare(this.age,obj.age);
        }
        //如果传入的形参不是User对象的实例,就抛出一个运行时提示
        throw new RuntimeException("传入的数据类型不一致");
    }
}

对象实现了Comparable接口并重写compareTo(obj)方法,指明了如何排序之后就可以重新再运行一下测试类了

import java.util.Arrays;
public class TestDemo {
    public static void main(String[] args) {
        //创建User对象的数组
        User[] users = new User[4];
        //往数组里面存放数据,放的是对象
        users[0] = new User("xiaohong",19);
        users[1] = new User("liming",16);
        users[2] = new User("zhangsan",18);
        users[3] = new User("wangwu",22);
        // 通过Arrays.sort方法,对users数组里面的元素进行排序
        Arrays.sort(users);
        //输出排序后的数组
        System.out.println(Arrays.toString(users));
        /**
         * 结果:
         *  [User{name='liming', age=16}, User{name='zhangsan', age=18}, User{name='xiaohong', age=19}, User{name='wangwu', age=22}]
         * 发现数据按照我们所指定的age进行了排序
         */
    }
}

如果比较的时候,比较的数据一样,那怎么样呢?那就接着再比, 这时就再去指定一下二级排序

package com.xun.pojo;
//实现 Comparable接口
public class User implements Comparable{
    private String name;
    private Integer age;
    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    //重写compareTo方法,并且指明如何排序
    @Override
    public int compareTo(Object o) {
        //判断o对象是否是 User对象的实例,是就返回true
        if (o instanceof User){
            //由于需要向下转型,所以需要强转
            User obj = (User) o;
        //方式一:
            //按照age进行排序
                //如果当前的age大于形参的age,就返回1
            if (this.age> obj.age){
                return 1;
                //如果当前的age小于形参的age,就返回-1
            }else if(this.age < obj.age){
                return -1;
                //否则就是age的数据相等,就再按照name进行从低到高排序
            }else{
                //this.name他是String类型,而String里面已经重写过了compareTo方法了,
                // 这时就可以直接调用String里面的compareTo方法了,传递形参进去
                //默认是从低到高排序,如果想从高到低,就在this前面加-,变成-this即可
                return this.name.compareTo(obj.name);
            }
        //方式二:
            //使用下面这种也行,包装类里面实现了Comparable接口,并且重写了compareTo方法,指明了如何排序
            //可以追加进去看看,发现也跟方式一差不多,传递两个参数,让着两个参数进行比较,然后根据判断的结果返回 -1、0、1
            //return Double.compare(this.age,obj.age);
        }
        //如果传入的形参不是User对象的实例,就抛出一个运行时提示
        throw new RuntimeException("传入的数据类型不一致");
    }
}

总结:

  • 如果像String、Integer…等包装类就不需要考虑去重写了,他已经重写过了,直接使用就可以了
  • 如果是自定义类的话那就需要去实现Comparable接口,并且重写compareTo方法指定怎么排序

定制排序

在什么情况下使用Comparator:

  • 当元素的类型没有实现 Comparable 接口而又不方便修改代码,或者实现了 Comparable 接口,但是排序的规则不适合当前的操作!那么可以考虑使用Comparator 的对象来排序,强行对多个对象进行整体排序的比较

使用Comparator接口规则:

  • 使用Comparator接口的时候需要重写 compare(Object o1,Object o2)方法,比较o1 和 o2 的大小【跟上面的compareTo方法规则类似】:如果方法返回正整数,则表示o1 大于 o2 ;如果返回 0,表示相等;返回负整数,表示o1 小于 o2。

使用Comparator接口方式:

  • 可以把Comparator传递给sort方法(如Collections.sort 或 Array.sort),从而允许在排序上实现精确控制

Comparator接口的使用场景:

  • 可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然排序的对象collecton提供排序。

测试:

//对String数组进行降序测试
    @Test
    public void test1(){
        String[] arr = new String[]{"aa","bb","ww","ff","ee","cc"};
        //由于String他实现了 Comparable接口,重写compareTo方法指定排序的顺序是升序的,
        //现在我们想给他降序又不能去修改String里面的compareTo方法,怎么办呢?
        //这时就需要使用到Comparator方法,定制排序。
        //这里使用Arrays.sort来实现定制排序,在sort()里丢一个数组,以及一个Comparator对象(这里使用的是匿名对象),需要重写
        //Comparator对象的方法compare,并且给他指定排序方式
        Arrays.sort(arr, new Comparator<String>() {
            //重写
            @Override
            public int compare(String o1, String o2) {
                //由于String已经重写好了compareTo,所以直接调用即可,然后在结果的前面加个负数 - ,
                //这样就达到了降序排序
                return -o1.compareTo(o2);
            }
        });
        System.out.println(Arrays.toString(arr));
        /**
         * 结果:
         *  [ww, ff, ee, cc, bb, aa]
         *  发现数据按照降序的顺序输出了
         */
    }


//对对象数组进行指定排序
    @Test
    public void test2(){
        //创建User对象的数组
        User[] users = new User[6];
        //往数组里面存放数据,放的是对象
        users[0] = new User("xiaohong",19);
        users[1] = new User("liming",16);
        users[2] = new User("zhangsan",18);
        users[3] = new User("zahangsan",18);
        users[4] = new User("wangwu",22);
        users[5] = new User("wangwu",25);
        //使用Arrays.sort进行定制排序,传递当前要排序的对象,以及传递Comparator对象,并重写compare方法指定排序
        Arrays.sort(users, new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
              //判断如果name一样就根据age来排序
                if (o1.getName().equals(o2.getName())){
                    //由于Double包装类里面实现了Comparable接口,并且重写了compareTo方法,指明了如何排序
                    //使用传递两个参数,让着两个参数进行比较,然后根据判断的结果返回 -1、0、1
                    return Double.compare(o1.getAge(),o2.getAge());
                }
                //如果不一样就根据name来进行排序
                else{
                    //由于String已经重写好了compareTo,所以直接调用即可
                    return o1.getName().compareTo(o2.getName());
                }
            }
        });
        System.out.println(Arrays.toString(users));
        /**
         * 结果:
         * [User{name='liming', age=16}, User{name='wangwu', age=22}, User{name='wangwu', age=25}, User{name='xiaohong', age=19}, User{name='zahangsan', age=18}, User{name='zhangsan', age=18}]
         * 可以看到数组先按照name进行排序,相等的话又按照age来进行排序
         */
    }

Comparable接口和Comparator接口的使用对比:

  • Comparable接口方式:实现了Comparable接口的实现类,在任何位置都可以比较大小。
  • Comparator接口方式:它属于临时性的比较,什么时候需要就去临时的指定一下。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,Comparator是一个接口,它定义了一个用于比较两个对象的方法compare(Object o1, Object o2)。Comparator接口通常用于对集合中的元素进行排序。 Comparator接口中的compare方法返回一个int值,表示两个对象的相对顺序。如果第一个对象小于第二个对象,则返回负数;如果第一个对象等于第二个对象,则返回0;如果第一个对象大于第二个对象,则返回正数。 Comparator接口的实现类可以使用匿名内部类、Lambda表达式或单独的类来创建。在比较两个对象时,可以使用任何属性或方法来定义它们之间的关系。 下面是一个使用Comparator接口对字符串进行排序的例子: ``` List<String> strings = Arrays.asList("apple", "orange", "banana", "pear"); Collections.sort(strings, new Comparator<String>() { public int compare(String s1, String s2) { return s1.compareTo(s2); } }); ``` 这个例子中,我们创建了一个字符串列表,并使用Collections.sort方法将其排序。我们使用了一个匿名内部类来实现Comparator接口,将字符串按字典序排序。 在Java中,比较器的顺序是根据返回值来确定的。如果compare方法返回负数,则第一个对象在第二个对象之前;如果compare方法返回正数,则第一个对象在第二个对象之后;如果compare方法返回0,则两个对象相等,它们的相对位置不变。 比较器的实现通常需要考虑到多个因素,例如按照价格排序时可能需要同时考虑产品的名称、品牌、型号等因素。在这种情况下,我们可以使用Java 8中引入的多字段排序方法,或者创建一个自定义比较器来实现排序。 总之,Comparator是Java中非常重要的一个接口,它提供了一种灵活的方法来对集合中的对象进行排序。熟练掌握Comparator接口的使用方法可以让我们更好地处理集合数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值