Comparable和Comparator的区别(Collections.sort和Arrays.sort)
Comparable
Comparable可以认为是一个 内比较器,实现了Comparable接口的类有一个特点,就是这些类是可以和自己比较的,至于具体和另一个实现了Comparable接口的类如何比较,
则依赖compareTo方法的实现,compareTo方法也被称为 自然比较方法。如果开发者add进入一个Collection的对象想要Collections的sort方法帮你自动进行排序的话,
那么这个对象必须实现Comparable接口。compareTo方法的返回值是int,有三种情况:
1、比较者大于被比较者(也就是compareTo方法参数里面的对象),那么返回正整数
2、比较者等于被比较者,那么返回0
3、比较者小于被比较者,那么返回负整数
Comparator
Comparator可以认为是是一个 外比较器 ,个人认为有两种情况可以使用实现Comparator接口的方式:
1、一个对象不支持自己和自己比较(没有实现Comparable接口),但是又想对两个对象进行比较
2、一个对象实现了Comparable接口,但是开发者认为compareTo方法中的比较方式并不是自己想要的那种比较方式
Comparator接口里面有一个compare方法,方法有两个参数T o1和T o2,是泛型的表示方式,分别表示待比较的两个对象,方法返回值和Comparable接口一样是int,有三种情况:
1、o1大于o2,返回正整数
2、o1等于o2,返回0
3、o1小于o3,返回负整数
概念看完了,直接上代码吧
public class Test {
public static void main(String[] args) {
/*
为什么需要比较器?
public interface Comparable<T>
该接口对实现它的每个类的对象强加一个整体排序。
@FunctionalInterface
public interface Comparator<T>
比较功能,对一些对象的集合施加了一个整体排序。
jdk1.8开发文档其实已经解释过了,主要是为了 给对象排序
*/
// 先从 String对象入手
String str1 = "aaaaa";
String str2 = "A";
System.out.println(str1+"和"+str2+"的compareTo结果: "+str1.compareTo(str2));
/*
为什么String类 对象str1.compareTo(str2)结果返回为 32
String 类已经实现了Comparable,重写了传入字符串比较的方法
public int compareTo(String anotherString) {
int len1 = value.length; //str1的长度
int len2 = anotherString.value.length; //str2的长度
int lim = Math.min(len1, len2); //返回 len1 和 len2 中小的那个数赋值給lim
char v1[] = value; // 定义暂时字符数组v1 指向 str1的value数组
char v2[] = anotherString.value; // 定义暂时字符数组v2 指向 str2的value数组
int k = 0;
while (k < lim) { //循环次数以 lim为准
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2; // a - A = 32
}
k++;
}
return len1 - len2;
}
*/
System.out.println("==================================================");
List list = new ArrayList();
list.add("bb");
list.add("aaaa");
list.add("e");
list.add("ccc");
list.add("fffff");
list.add("ddd");
System.out.println("原列表顺序:" + list);
Collections.sort(list);
System.out.println("默认排序: " + list);
/*
因为我调用的Collections.sort(list);没有传入自定义的比较器,所以会使用第一种默认的sort
//第一种 sort(List<T> list)
//第二种 sort(List<T> list, Comparator<? super T> c)
//debug...F7
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
//debug...F7
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c); // 会发现c为null时Collections.sort()在默认的情况下会使用Arrays.sort()
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
//debug...(Arrays.sort((E[]) elementData, 0, size, c))F7
public static <T> void sort(T[] a, int fromIndex, int toIndex,
Comparator<? super T> c) {
if (c == null) {
sort(a, fromIndex, toIndex); //因为c为null没有比较器,会走这个sort
} else {
rangeCheck(a.length, fromIndex, toIndex);
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, fromIndex, toIndex, c);
else
TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0);
}
}
//debug...(sort(a, fromIndex, toIndex);)F7
public static void sort(Object[] a, int fromIndex, int toIndex) {
rangeCheck(a.length, fromIndex, toIndex);
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, fromIndex, toIndex);//LegacyMergeSort(遗留下来的归并排序)
else
ComparableTimSort.sort(a, fromIndex, toIndex, null, 0, 0);//一般sort会走这个方法
}
//debug...(ComparableTimSort.sort(a, fromIndex, toIndex, null, 0, 0))F7
static void sort(Object[] a, int lo, int hi, Object[] work, int workBase, int workLen) {
assert a != null && lo >= 0 && lo <= hi && hi <= a.length;
int nRemaining = hi - lo;
if (nRemaining < 2)
return; // Arrays of size 0 and 1 are always sorted
// If array is small, do a "mini-TimSort" with no merges
if (nRemaining < MIN_MERGE) {
int initRunLen = countRunAndMakeAscending(a, lo, hi);
binarySort(a, lo, hi, lo + initRunLen);
return;
}
ComparableTimSort ts = new ComparableTimSort(a, work, workBase, workLen);
int minRun = minRunLength(nRemaining);
do {
// Identify next run
int runLen = countRunAndMakeAscending(a, lo, hi);
// If run is short, extend to min(minRun, nRemaining)
if (runLen < minRun) {
int force = nRemaining <= minRun ? nRemaining : minRun;
binarySort(a, lo, lo + force, lo + runLen); // 会走这个方法!!!!!!
runLen = force;
}
// Push run onto pending-run stack, and maybe merge
ts.pushRun(lo, runLen);
ts.mergeCollapse();
// Advance to find next run
lo += runLen;
nRemaining -= runLen;
} while (nRemaining != 0);
// Merge all remaining runs to complete sort
assert lo == hi;
ts.mergeForceCollapse();
assert ts.stackSize == 1;
}
//debug...(binarySort(a, lo, lo + force, lo + runLen))F7
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];
// Set left (and right) to the index where a[start] (pivot) belongs
int left = lo;
int right = start;
assert left <= right;
while (left < right) {
int mid = (left + right) >>> 1;
if (pivot.compareTo(a[mid]) < 0) //会走String的compareTo()方法
right = mid;
else
left = mid + 1;
}
assert left == right;
int n = start - left; // The number of elements to move
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;
}
}
*/
Collections.sort(list, new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
return ((String) o1).length() - ((String) o2).length();//根据字符串长度排序
}
});
System.out.println("根据字符串长度排序: " + list);
System.out.println("==================================================");
Person person1 = new Person("A", 12);
Person person2 = new Person("BBB", 8);
Person person3 = new Person("CC", 20);
Person person4 = new Person("EEEE", 1);
List plist = new ArrayList();
plist.add(person1);
plist.add(person2);
plist.add(person3);
plist.add(person4);
System.out.println("原(Person)列表顺序:" + plist);
Collections.sort(plist);
System.out.println("原(Person)列表根据年龄大小排序: " + plist);
Collections.sort(plist,new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getName().length() - o2.getName().length();//根据字符串长度排序
}
});
System.out.println("原(Person)列表根据名字长度排序: " + plist);
//根据以下结果可以看出,如果我们对内比较器Comparable算法不满意除了重写内比较器算法,还可以通过外比较器Comparator重新定义一个外比较器算法,覆盖内比较器
}
}
class Person implements Comparable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Object o) {
return this.age - ((Person) o).age;//根据年纪排序
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
运行结果如下:
aaaaa和A的compareTo结果: 32
==================================================
原列表顺序:[bb, aaaa, e, ccc, fffff, ddd]
默认排序: [aaaa, bb, ccc, ddd, e, fffff]
根据字符串长度排序: [e, bb, ccc, ddd, aaaa, fffff]
==================================================
原(Person)列表顺序:[Person{name='A', age=12}, Person{name='BBB', age=8}, Person{name='CC', age=20}, Person{name='EEEE', age=1}]
原(Person)列表根据年龄大小排序: [Person{name='EEEE', age=1}, Person{name='BBB', age=8}, Person{name='A', age=12}, Person{name='CC', age=20}]
原(Person)列表根据名字长度排序: [Person{name='A', age=12}, Person{name='CC', age=20}, Person{name='BBB', age=8}, Person{name='EEEE', age=1}]
总结
总结一下,两种比较器Comparable和Comparator,后者相比前者有如下优点:
1、如果实现类没有实现Comparable接口,又想对两个类进行比较(或者实现类实现了Comparable接口,但是对compareTo方法内的比较算法不满意),那么可以实现Comparator接口,自定义一个比较器,重写比较算法
2、实现Comparable接口的方式比实现Comparator接口的耦合性 要强一些,如果要修改比较算法,要修改Comparable接口的实现类,而实现Comparator的类是在外部进行比较的,不需要对实现类有任何修改。
所以开发者还是需要根据具体场景选择最合适的那种比较器。