关于Comparable、Comparator接口返回值决定顺序的问题

Comparable和Comparator接口都是实现集合中元素的比较、排序的,下面先简单介绍下他们的用法。

1. 使用示例

public class Person {

	private String name;
	private Integer age;
	public Person() {}
	public Person(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 "Person{" +
				"name='" + name + '\'' +
				", age=" + age +
				'}';
	}

	@Override
	public int compareTo(Person o) {
		return  this.age - o.age;
	}
}
        ArrayList<Person> list = new ArrayList<>();

        list.add(new Person("朱一百",100));
        list.add(new Person("朱八零",80));
        list.add(new Person("朱八一",81));
        list.add(new Person("朱九零",90));
        list.add(new Person("朱八二",82));
        list.add(new Person("朱七七",77));
        list.add(new Person("朱八八",88));
        list.add(new Person("朱重八",88));

1.1 Comparable

public class Person  implements Comparable<Person>{
	......
    @Override
    public int compareTo(Person o) {
        if (this.getAge() < o.getAge()) {
            return -1;
        } else if (this.getAge() > o.getAge()) {
            return 1;
        } else {
            return this.getName().compareTo(o.getName());
        }
    }
}
public class ComparableTest {
    public static void main(String[] args) {
    	......
        Collections.sort(list);
        list.forEach(System.out::println);
    }
}

1.2 Comparator

public class PersonComparator  implements Comparator<Person> {
    @Override
    public int compare(Person o1, Person o2) {
        if (o1.getAge() < o2.getAge()) {
            return -1;
        } else if (o1.getAge() > o2.getAge()) {
            return 1;
        } else {
            return o1.getName().compareTo(o2.getName());
        }
    }
}
public class ComparatorTest {
    public static void main(String[] args) {
    	......
        Collections.sort(list,new PersonComparator());
        list.forEach(System.out::println);
    }
}

以上的两个例子输出的结果如下所示:

Person{name='朱七七', age=77}
Person{name='朱八零', age=80}
Person{name='朱八一', age=81}
Person{name='朱八二', age=82}
Person{name='朱八八', age=88}
Person{name='朱啊八', age=88}
Person{name='朱九零', age=90}
Person{name='朱一百', age=100}

按照年龄升序排列,年龄相同时按照姓名升序排序。

2. 关于返回结果-1 0 1 和排序的关系

在上面的例子中我们分别实现了compare()compareTo()对集合继续宁排序,但是我们想知道方法返回-1、0、1对集合排序的关系。

2.1 Comparable

List#sort() =>ArrayList#sort() => Arrays#sort() ⇒ ComparableTimSort#sort() ⇒ ComparableTimSort.#countRunAndMakeAscending()
ArrayList#sort()
可以看到到Comparator c == null 时进入sort() 方法,当我们实现Comparator进行排序时,进入的方法就应该是 TimSort.sort()
Arrays#sort()
ComparableTimSort#sort()
ComparableTimSort.#countRunAndMakeAscending()
countRunAndMakeAscending方法如下:

    private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {
        assert lo < hi;
        int runHi = lo + 1;
        if (runHi == hi)
            return 1;
        
        if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descending
            while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)
                runHi++;
            reverseRange(a, lo, runHi);
        } else {                              // Ascending
            while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0)
                runHi++;
        }

        return runHi - lo;
    }

如果当前子序列是"递减"的,也就是 a[runHi] 小于 a[lo],那么需要找到连续"递减"的子序列的结束位置 runHi。直到找到第一个不满足"递减"条件的元素。然后,调用 reverseRange 方法将"递减"的子序列反转为"递增"的子序列。

如果当前子序列是"递增"的,也就是 a[runHi] 大于等于 a[lo],那么需要找到连续"递增"的子序列的结束位置 runHi。
最后,返回 runHi - lo,即子序列的长度。

    private static void binarySort(Object[] a, int lo, int hi, int start) {
        assert lo <= start && start <= hi;
        if (start == lo)
            start++;
        for ( ; start < hi; start++) {
            Comparable pivot = (Comparable) a[start];

            int left = lo;
            int right = start;
            assert left <= right;
            /*
             * Invariants:
             *   pivot >= all in [lo, left).
             *   pivot <  all in [right, start).
             */
            while (left < right) {
                int mid = (left + right) >>> 1;
                // pivot元素的索引比a[mid]的索引大
                // pivot元素在数组中比a[mid]靠后
                if (pivot.compareTo(a[mid]) < 0)
                    right = mid;
                else
                    left = mid + 1;
            }
            assert left == right;
            
            int n = start - left;  // The number of elements to move
            // Switch is just an optimization for arraycopy in default case
            switch (n) {
                case 2:  a[left + 2] = a[left + 1];
                case 1:  a[left + 1] = a[left];
                         break;
                default: System.arraycopy(a, left, a, left + 1, n);
            }
            a[left] = pivot;
        }
    }

这个方法做的事情如下:

  1. 经过countRunAndMakeAscending()方法 start位置之前的集合是有序的,于是start位置开始,pivot=array[start],使用二分查找法对start之前已经有序的数组比对,从start下标开始从后往前找,找到start下标之前第一个大于array[start]的元素下标index;
  2. 根据要移动的步数n = start - left做了优化,但是目的是将[left, start-1]范围内的数组值都向后移动一位,放在 [left+1~start],再把pivot的值赋予array[left];
  3. start++,循环执行以上步骤直到数组结束

总结

我们实现的compareTo()方法返回-1 0 1 和集合的排序是升序或者降序没有直接的关系,在上面的方法我们可以看到的是compareTo()返回会导致如下结果:

  1. 返回-1说明两个比较的元素需要交换位置
  2. 但会0或者1不需要交换两个元素的位置

所以我们应该能比较清楚的知道返回-1 0 1 给我们排序带来的效果了,比如上方的pivot.compareTo(a[mid]) pivot在数组中比a[mid]靠后,当 if (this.getAge() < o.getAge()) {return -1}; 我们希望pivot的age小于a[mid]时交换位置,最后排序的结果就是年龄的升序,如果if (this.getAge() < o.getAge()) {return 1};最后排序的结果就是年龄的降序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: compareTo和Comparable都是Java中用于比较对象的接口和方法。 Comparable是一个接口,它定义了一个compareTo()方法,用于比较对象的大小。实现Comparable接口的类可以使用Collections.sort()方法进行排序。 compareTo()方法返回一个整数值,表示当前对象与另一个对象的大小关系。如果当前对象小于另一个对象,则返回负整数;如果当前对象等于另一个对象,则返回;如果当前对象大于另一个对象,则返回正整数。 例如,如果我们有一个Student类实现了Comparable接口,我们可以使用以下代码对学生列表进行排序: List<Student> students = new ArrayList<>(); students.add(new Student("Tom", 20)); students.add(new Student("Jerry", 18)); Collections.sort(students); 这将根据学生的年龄对学生列表进行排序。 compareTo()方法的实现可以根据需要进行自定义,以实现不同的比较逻辑。 相比之下,Comparator是一个接口,它定义了一个compare()方法,用于比较两个对象的大小。Comparator接口可以用于对任何类的对象进行排序,而不需要实现Comparable接口。 与compareTo()方法不同,compare()方法需要接收两个参数,分别是要比较的两个对象。compare()方法的返回值compareTo()方法相同,表示两个对象的大小关系。 例如,如果我们有一个Student类,但它没有实现Comparable接口,我们可以使用以下代码对学生列表进行排序: List<Student> students = new ArrayList<>(); students.add(new Student("Tom", 20)); students.add(new Student("Jerry", 18)); Comparator<Student> ageComparator = new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o1.getAge() - o2.getAge(); } }; Collections.sort(students, ageComparator); 这将根据学生的年龄对学生列表进行排序,而不需要Student类实现Comparable接口。 总之,compareTo和ComparableJava中用于比较对象的接口和方法,它们可以帮助我们对对象进行排序和比较。其中,Comparable接口适用于实现了Comparable接口的类,而Comparator接口适用于任何类的对象。 ### 回答2: "compareTo" 和 "comparable" 是Java中用于比较对象的两个相关概念。 首先,"comparable" 是一个接口,当一个类实现了 "comparable" 接口时,它表明该类的实例是可比较的。实现 "comparable" 接口的类必须重写 "compareTo" 方法,该方法根据对象的特定属性来定义比较规则。该方法需要返回一个整数值,表示比较结果的正负或零。通过实现 "comparable" 接口,我们可以在使用 "Collections" 类进行排序等操作时对对象进行比较。 而 "compareTo" 是一个用于比较两个对象的方法。它可以在一个实现了 "comparable" 接口的类的对象上调用。 "compareTo" 方法接受一个对象作为参数,并根据实现类中定义的比较规则,比较当前对象与参数对象。如果当前对象小于参数对象,方法会返回负数;如果当前对象大于参数对象,方法会返回正数;如果两个对象相等,方法会返回零。 总结起来,"comparable" 是一个接口,用于标识一个类的实例可比较,并要求实现类必须重写 "compareTo" 方法。"compareTo" 方法则是在实现了 "comparable" 接口的类的对象上调用,用于比较对象并返回比较结果的方法。通过实现 "comparable" 接口和重写 "compareTo" 方法,我们可以实现对象的自定义比较规则,并在排序、集合操作等情况下使用。 ### 回答3: compareTo和ComparableJava中用于比较对象的两个概念。 compareTo是一个方法,用于实现对象之间的比较。它是Comparable接口中定义的方法之一。compareTo方法接收一个参数,用于与当前对象进行比较。它根据对象的属性值来判断两个对象的大小关系。如果当前对象小于传入的对象,则返回负数;如果当前对象等于传入的对象,则返回0;如果当前对象大于传入的对象,则返回正数。这个方法常常用于对对象进行排序,比如使用Collections.sort方法对一个实现了Comparable接口的类的对象进行排序。 Comparable是一个接口,用于比较对象的大小。实现了Comparable接口的类必须实现compareTo方法,来定义对象的比较规则。一般来说,实现了Comparable接口的类的对象可以进行自然排序,即按照某种规则进行排序。比如,对于整数而言,自然排序是按照数值的大小进行排序;对于字符串而言,自然排序是按照字典顺序进行排序。当我们需要对某个类的对象进行排序时,可以让该类实现Comparable接口,然后通过compareTo方法进行比较和排序。 综上所述,compareTo和ComparableJava中用于比较对象的两个概念。其中,compareTo是一个方法,用于实现对象之间的比较;Comparable是一个接口,用于定义对象的自然排序规则。通过实现Comparable接口并实现compareTo方法,我们可以对实现了Comparable接口的类的对象进行比较和排序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值