Java学习49-Comparator比较器 & Comparable接口

Comparator比较器 & Comparable接口

java想要比较基本数据类型,比如两个int可以直接用 > < != <= >= 等进行处理,但是如果要比较Object,可以选用下面两种接口Comparable或Comparator,其中Comparable 是一个泛型接口,它有一个类型为 T 的唯一方法 compareTo(T o)。任何实现了 Comparable 接口的类的对象将被排序。注意接口只表明有输入object的接收端,具体怎么完成比较两个object,如果系统没有帮忙定义,用户需要自定义对应的比较功能的method,用户经常override 重写compareTo方法。

下面是Comparable interface(接口)的定义文件


package java.lang;
import java.util.*;
public interface Comparable<T> {
   public int compareTo(T o);
}

总结:Java中对多个object进行排序,其实就是比较object下属某种属性的大小,用下面两个接口中的任意一个:Comparable或者Comparator,注意用的时候需要完成对应的method比如compareTo,同时在文件顶打上Java包:
自然排序: java.lang.Comparable
定制排序: java.util.Comparator

自然排序Comparable接口

  1. 自然排序Comparable接口 使用举例:像String类,包装类等,系统已经帮用户默默实现了comparable接口,并且重写了compareTo()方法,用户可以快速调用直接,比较两个对象的大小。

下面看一个简单的例子

package Compare;

import java.util.Arrays;
import java.util.TreeSet;

public class test1 {
    public static void main(String[] args) {
        String [] x=new String[]{"ss","dd","aa","cc"};
        Arrays.sort(x);
        System.out.println(Arrays.toString(x));
    }
}

输出结果

[aa, cc, dd, ss]

上面例子可以看出,系统重新排列了输入的Arrays,Comparable接口在哪使用了呢?

双击String关键字打开String文件,可以看见String标题定义处标明了Comparable接口,使用Ctrl+f在文件内搜索compareTo果然见到了compareTo method的具体定义,如下所示:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {

    其他的程序部分略略略

 public int compareTo(String anotherString) {
        byte v1[] = value;
        byte v2[] = anotherString.value;
        byte coder = coder();
        if (coder == anotherString.coder()) {
            return coder == LATIN1 ? StringLatin1.compareTo(v1, v2)
                                   : StringUTF16.compareTo(v1, v2);
        }
        return coder == LATIN1 ? StringLatin1.compareToUTF16(v1, v2)
                               : StringUTF16.compareToLatin1(v1, v2);
     }


    其他的程序部分略略略

	}

看得出来,String类已经默默帮用户实现了接口Constable,并且写好了这个接口里面唯一的method compareTo(obj)。

  1. Comparable接口的CompareTo()重写规则(默认小到大顺序排列)
    如果当前对象this大于形参对象obj 则返回正数
    如果当前对象this小于形参对象obj 则返回负数
    如果当前对象this等于形参对象obj 则返回零

  2. 对于自定义类来说,如果需要排序,可以让自定义类实现comparable接口,override重写 CompareTo() method,下面举例说明

用户自定义了Goods商品类,里面是一些商品,属性有name和price,可能调用到的ToString和CompareT method,用户将其全部重写了(Goods类的定义处需要写上implements Comparable<Goods> )。

package Compare;

public class Goods implements Comparable<Goods>{
    private String name;
    private double price;

    public Goods() {
    }

    public Goods(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

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

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

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

/* 简单写法
    @Override
    public int compareTo(Goods o) {
        if (this.price > o.price)
        return 1;
        else if (this.price < o.price)
            return -1;
        else return 0;
    }
*/

  @Override
    public int compareTo(Goods obj1) {
        //先看两个obj有没有父子关系,没关系不比较,直接报错
        if(obj1 instanceof Goods){
            Goods myobj = (Goods)obj1;
            /* 判断方法一:
            if (this.price > myobj.price)
            return 1;
            else if (this.price < myobj.price)
                return -1;
            else return 0;
           */
            //判断方法二:
            return Double.compare(this.price,myobj.price);
        }
        throw new RuntimeException("输入类型不一致,无法比较!");
    }


}

主程序定义了几个带有属性name和price的Goods,并且尝试用Arrays.sort()来排序。从理论上讲
Arrays.sort()将会调用到ToString和CompareT method,用户将这些需要的method全部重写了。

import java.util.Arrays;
import java.util.TreeSet;

public class test1 {
    public static void main(String[] args) {
        System.out.println("=====分隔线====>");
        Goods[] y = new Goods[4];
        y[0]=new Goods("lenovo",32);
        y[1]=new Goods("intel",22);
        y[2]=new Goods("microsoft",42);
        y[3]=new Goods("asus",21);
        Arrays.sort(y);
        System.out.println(Arrays.toString(y));
    }
}

生成结果:

=====分隔线====>
[Goods{name='asus', price=21.0}, Goods{name='intel', price=22.0}, Goods{name='lenovo', price=32.0}, Goods{name='microsoft', price=42.0}]

Process finished with exit code 0

如果price一样,那么CompareTo输出均为0,用户还需比较名称才能确定前后排序。名称排序只需修改return 0部分为


 public int compareTo(Goods obj1) {
        //先看两个obj有没有父子关系,没关系不比较,直接报错
        if(obj1 instanceof Goods){
            Goods myobj = (Goods)obj1;
            // 判断方法一:
            if (this.price > myobj.price)
            return 1;
            else if (this.price < myobj.price)
                return -1;
            else {
                return this.name.compareTo(myobj.name);
                //还想根据名称继续深度排序,修改此处return 0;为上面的语句
            }

            //判断方法二:
            //return Double.compare(this.price,myobj.price);

        }

        throw new RuntimeException("输入类型不一致,无法比较!");
    }

将商品定义价格略微修改,便于验证排序功能:


      Goods[] y = new Goods[4];
        y[0]=new Goods("lenovo",32);
        y[1]=new Goods("intel",22);
        y[2]=new Goods("microsoft",31);
        y[3]=new Goods("asus",31);

运行结果

=====分隔线====>
[Goods{name='intel', price=22.0}, Goods{name='asus', price=31.0}, Goods{name='microsoft', price=31.0}, Goods{name='lenovo', price=32.0}]

Process finished with exit code 0


简单的原理,如果想要字母从Z-A排序,直接在this前加个“-”号

    else {
                return -this.name.compareTo(myobj.name);
               
            }

如此,结果变成了microsoft在asus前

=====分隔线====>
[Goods{name='intel', price=22.0}, Goods{name='microsoft', price=31.0}, Goods{name='asus', price=31.0}, Goods{name='lenovo', price=32.0}]

Process finished with exit code 0

定制排序Comparator接口(需要重写compare方法)

  1. 背景:当元素类型没法用java.lang.Comparable接口,也不方便修改对接方的代码;或者实现了java.lang.Comparable接口,排列顺序却不是用户想要的;都可以考虑Comparator接口来排序。

  2. 实现步骤:

  • 创建一个实现了Comparator接口的实现类A
  • 实现类A要求重写Comparator接口中的抽象方法compare(obj1, obj2),此方法中指明要比较大小的对象的大小关系。(比如String类,Product类)
  • 创建此实现类A的对象,并将此对象传入到相关方法的参数位置即可。比如Arrays.sort(…,类A的实例)
  1. 对比两种方式:
    角度一:自然排序:单一的,唯一的
    定制排序:灵活的,多样的

角度二:
自然排序:一劳永逸的
定制排序:临时的

角度三:细节
自然排序:对应的接口是Comparable,对应的抽象方法compareTo(Object obj),用的时候写obj1.compareTo(Object obj2)
定制排序:对应的接口是Comparator,对应的抽象方法compare(Object obj1,Object obj2)

举例1,下面的例子在String.sort() method里使用了Comparator并且重写了对应的compare method

package Compare;

import java.util.Arrays;
import java.util.Comparator;

public class test2 {
    public static void main(String[] args) {
        String[] arr=new String[] {"ss","dd","aa","cc","vv","tt"};

        Arrays.sort(arr, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof String && o2 instanceof String){
                    String s1 = (String) o1;
                    String s2 = (String) o2;
                    return -(s1.compareTo(s2));
                }
                throw new RuntimeException("输入不是一个类型");
            }
        });

        System.out.println(Arrays.toString(arr));

        }
}


示例2,分别以price,name来对几个Product进行排序,参考代码如下:


package Arrays;

public class Product implements Comparable{
    public Product() {
    }

    public Product(String name, int id, double price) {
        this.name = name;
        this.id = id;
        this.price = price;
    }

    String name;
    int id;
    double price;

    public String getName() {
        return name;
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Product{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", price=" + price +
                '}';
    }

    @Override
    public int compareTo(Object o) {
        if(o == this) {
            return 0;
        }
        if(o instanceof Product){

            
            Product p = (Product) o;
            int re = Double.compare(p.price,this.price);
            if(re != 0 ){
                return re;
            }
           return -this.name.compareTo(p.name);

        }
        throw new RuntimeException("类型不匹配");
    }
}


主程序如下:

package Arrays;
import org.junit.Test;

import java.util.Arrays;
import java.util.Comparator;

public class ComparatorTest {
    public static void main(String[] args) {
        String [] arr = new String[]{"one","of","my","fav","food","this","is","lol"};
        //Arrays.sort(arr);
        Arrays.sort(arr, new Comparator<Object>() {
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof String && o1 instanceof String)
                {String s1 = (String) o1;
                String s2 = (String) o2;
                return -s1.compareTo(s2);
                }
                throw new RuntimeException("类型不同无法比较");
            }
        });

        System.out.println(Arrays.toString(arr));



    }

    @Test

    public void test01(){
        Product [] arr = new Product[5];
        arr[0] = new Product("Huawei",123,4999);
        arr[1] = new Product("Xiaomi",213,4999);
        arr[2] = new Product("iPhone",323,9999);
        arr[3] = new Product("Vivo",423,1999);
        arr[4] = new Product("eeSamsung",523,6999);

        //创建了一个实现了Comparator接口的实现类的对象
       Comparator comparator1 = new Comparator() {

           //如果判断两个对象o1,o2的大小,其标准就是此方法的方法体要编写的内容。
           @Override
           public int compare(Object o1, Object o2) {
               if(o1 instanceof Product && o2 instanceof Product){
                   if(((Product) o1).price != ((Product) o2).price){
                       return (int) (((Product) o1).price-((Product)o2).price);
                   }
                   return ((Product) o1).name.compareTo(((Product) o2).name);
               }
               throw new RuntimeException("非Product内容 无法比较");
           }
       };

       Comparator comparator2 = new Comparator() {
           //如果判断两个对象o1,o2的大小,其标准就是此方法的具体编写的逻辑。
           //比如:按照价格从低到高排序
           //如果想要由高到低排序,就用个负号,比如-Double.compare(p1.getPrice(),p2.getPrice());
           @Override
           public int compare(Object o1, Object o2) {
               if (o1 instanceof Product && o2 instanceof Product){
                   Product p1 = (Product) o1;
                   Product p2 = (Product) o2;
                   return Double.compare(p1.getPrice(),p2.getPrice());
               }
               throw new RuntimeException("类型不匹配");
           }
       };


       /*
       *


       //使用了comparator2的比较模式进行比较运算
       Arrays.sort(arr,comparator2);
       // System.out.println(Arrays.toString(arr));

        for (Product each : arr) {
            System.out.println(each);
        }
 *
       * */

        Comparator com3 = new Comparator() {
            //如果判断两个对象o1,o2的大小,其标准就是此方法的具体编写的逻辑。
            //比如:按照name排序

            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof Product && o2 instanceof Product){
                    Product p1 = (Product) o1;
                    Product p2 = (Product) o2;

                return (p1.getName().compareTo( p2.getName()));
                }
                throw new RuntimeException("类型不对,比较不了,告辞");
            }
        };


        Arrays.sort(arr,com3);


        for (Product each : arr) {
            System.out.println(each);
        }


    }

}

示例3,对一些String进行排序,新建new Comparator并且直接重写了compare方法,参考代码如下:

package Arrays;
import org.junit.Test;

import java.util.Arrays;
import java.util.Comparator;

public class ComparatorTest {
@Test
    public void test2(){
    String [] arr = new String[]{"one","of","my","fav","food","this","is","lol"};
    Arrays.sort(arr, new Comparator<Object>() {
        @Override
        public int compare(Object o1, Object o2) {
            if (o1 instanceof String && o2 instanceof String){
                return ((String) o1).compareTo((String) o2);
            }
            throw new RuntimeException("不是String类型");
        }
    });

    for (String each : arr) {
        System.out.print(each+"\t");
    }

    }

}

  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值