comparable和comparator

Comparable是排序接口。若一个类实现了Comparable接口,就意味着该类支持排序。实现了Comparable接口的类的对象的列表或数组可以通过Collections.sort或Arrays.sort进行自动排序。

public interface Comparable<T> 
{
    public int compareTo(T o);
}

具体的使用看一个例子就明白,很简单

public class Person implements Comparable<Person>
{
    String name;
    int age;
    public Person(String name, int age)
    {
        super();
        this.name = name;
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public int getAge()
    {
        return age;
    }
    @Override
    public int compareTo(Person p)   //这里我们实现这个方法,并在里面添加比较的标准是什么  
    {
        return this.age-p.getAge();
    }
    public static void main(String[] args)
    {
        Person[] people=new Person[]{new Person("xujian", 20),new Person("xiewei", 10)};
        System.out.println("排序前");
        for (Person person : people)
        {
            System.out.print(person.getName()+":"+person.getAge());
        }
        Arrays.sort(people);
        System.out.println("\n排序后");
        for (Person person : people)
        {
            System.out.print(person.getName()+":"+person.getAge());
        }
    }
}

关于这个比较的标准

@Override
    public int compareTo(Person p)   
    {
        return this.age-p.getAge();
    }

意思是如果当前对象的age更大,那么返回一个正值,那么排序就是升序排序,如果我返回的是负值那么就是降序排序。为什么是这样呢,因为这里有一个规定:

compareTo返回1就会交换比较对象的位置,其他不会交换。

所以this.age-p.getAge();,如果当前对象age减去比较对象age大于0,然后交换当前对象到比较对象后面,说明就是升序。反之不交换就是降序

那么对于我们这个例子的解释,如果前一个对象的age大于比较对象的age,那么我们返回一个正值,符合升序的条件,所以这个排序的标准就是按照升序排序。如果改成

@Override
    public int compareTo(Person p)   
    {
        return -(this.age-p.getAge());
    }

前一个对象的age大于比较对象的age,那么我们返回一个负值,满足的是降序的条件,那么排序的准则就是降序。

Comparator是比较接口,我们如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序,这个“比较器”只需要实现Comparator接口即可。也就是说,我们可以通过实现Comparator来新建一个比较器,然后通过这个比较器对类进行排序。该接口定义如下:

package java.util;
public interface Comparator<T>
 {
    int compare(T o1, T o2);
    boolean equals(Object obj);
 }

对于该接口的排序的准则上面已经说的很清楚了,一模一样。

举一个应用实例:

有一个英文的字符串,字符串有很多的单词,每个单词之间用空格隔开,就相当于是一个英文文章一样。那么这篇文章的单词有长有短,要求输出每一个单词,同时输出的顺序的按照单词的长度递增输出。首先肯定的是直接用split就可以将文章转换成一个字符串数组,那么剩下的就是对这个数组进行操作。直接用排序就ok,下面是一个最快捷的思路演示:

     LinkedList<String> list =new LinkedList<>();
    list.add("shi");
    list.add("xiaoxiao");
    list.add("xiao");
    list.add("xiao");
    list.add("shi");
    list.add("xiaopang");
    list.add("dapang");
    list.add("haha");
    

    
    Collections.sort(list,new Comparator<String>() {

        @Override
        public int compare(String o1, String o2) {
            // TODO Auto-generated method stub
            return o1.length()-o2.length();
        }

    });

输出list:

shi
shi
haha
xiao
xiao
dapang
xiaopang

xiaoxiao  //字符串长度递增

第二个实例,我们对一个二维数组进行排序,排序的标准是第二列。

    String[][] arr={{"s", "2", "aaa"},{"r", "1" ,"bbb"},{"s" ,"0", "ccc"}};
    Arrays.sort(arr, new Comparator<String[]>(){
        @Override
        public int compare(String[] o1, String[] o2) {
            return o1[1].compareTo(o2[1]);
        }
    });
    
    
    for(String[] s:arr){
        System.out.println(Arrays.toString(s));

    }

原先的数组是:

[s, 2, aaa]
[r, 1, bbb]

[s, 0, ccc]

排序结束之后为:

[s, 0, ccc]
[r, 1, bbb]
[s, 2, aaa]

临时表分组,排序,尽量用java8新特性stream进行处理

使用java8新特性,下面先来点基础的

List<类> list; 代表某集合

//返回 对象集合以类属性一升序排序

list.stream().sorted(Comparator.comparing(类::属性一));

//返回 对象集合以类属性一降序排序 注意两种写法

list.stream().sorted(Comparator.comparing(类::属性一).reversed());//先以属性一升序,结果进行属性一降序

list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()));//以属性一降序

//返回 对象集合以类属性一升序 属性二升序

list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二));

//返回 对象集合以类属性一降序 属性二升序 注意两种写法

list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二));//先以属性一升序,升序结果进行属性一降序,再进行属性二升序

list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二));//先以属性一降序,再进行属性二升序

//返回 对象集合以类属性一降序 属性二降序 注意两种写法

list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,升序结果进行属性一降序,再进行属性二降序

list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一降序,再进行属性二降序

//返回 对象集合以类属性一升序 属性二降序 注意两种写法

list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二).reversed());//先以属性一升序,升序结果进行属性一降序,再进行属性二升序,结果进行属性一降序属性二降序

list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,再进行属性二降序

 

通过以上例子我们可以发现

  1. Comparator.comparing(类::属性一).reversed();

  2. Comparator.comparing(类::属性一,Comparator.reverseOrder());

两种排序是完全不一样的,一定要区分开来 1 是得到排序结果后再排序,2是直接进行排序,很多人会混淆导致理解出错,2更好理解,建议使用2

实际例子:

现有一个类test 有两个属性:state 状态 time 时间,需要状态顺序且时间倒序

//状态
private int state;

//时间
private Date time;

public test(int state, Date time) {
    this.state = state;
    this.time = time;
}

public int getState() {
    return state;
}

public void setState(int state) {
    this.state = state;
}

public Date getTime() {
    return time;
}



public void setTime(Date time) {
    this.time = time;
}

@Override

public String toString() {

    return "test{" +

            "state=" + state +

            ", time=" + DateUtils.formatDateYMD(time) +

            '}';
}
public static void main(String[] args) {

    List<test> testList = new ArrayList<>();

    Date d = DateUtils.now();

    for (int i = 1; i <= 3; i++) {

        test t = new test(i, DateUtils.addDays(d, i));

        testList.add(t);

    }
    for (int i = 1; i <= 3; i++) {

        test t = new test(i, DateUtils.addMonths(d, i));

        testList.add(t);

    }
    testList.forEach(o -> {

        System.out.println(o.toString());

    });

    List<test> sort = testList.stream().sorted(Comparator.comparing(test::getState).thenComparing(test::getTime,Comparator.reverseOrder())).collect(toList());

    System.out.println("------------------------------------");

    sort.forEach(o -> {

        System.out.println(o.toString());

    });
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值