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接口
- 自然排序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)。
-
Comparable接口的CompareTo()重写规则(默认小到大顺序排列)
如果当前对象this大于形参对象obj 则返回正数
如果当前对象this小于形参对象obj 则返回负数
如果当前对象this等于形参对象obj 则返回零 -
对于自定义类来说,如果需要排序,可以让自定义类实现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方法)
-
背景:当元素类型没法用java.lang.Comparable接口,也不方便修改对接方的代码;或者实现了java.lang.Comparable接口,排列顺序却不是用户想要的;都可以考虑Comparator接口来排序。
-
实现步骤:
- 创建一个实现了Comparator接口的实现类A
- 实现类A要求重写Comparator接口中的抽象方法compare(obj1, obj2),此方法中指明要比较大小的对象的大小关系。(比如String类,Product类)
- 创建此实现类A的对象,并将此对象传入到相关方法的参数位置即可。比如Arrays.sort(…,类A的实例)
- 对比两种方式:
角度一:自然排序:单一的,唯一的
定制排序:灵活的,多样的
角度二:
自然排序:一劳永逸的
定制排序:临时的
角度三:细节
自然排序:对应的接口是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");
}
}
}