我们在学习java基础的时候知道,基本数据类型数组可以直接调动Arrays类的静态sort方法,然后输出。
例如: int iArr[] = {1,2,4,6}; Arrays.sort(iArr); 然后利用for循环输出..
但是如果我们是对象数组的话,则对象所在的类必须实现comparable接口,覆写它的compareTo方法,并且要有不同的返回值,代表升序和降序。
但是会有一个疑问:为什么对象数组调用Arrays.sort 必须要实现comparable接口呢?而且compareTo的返回值到底代表什么意义呢,它又是如何决定数组的升序和降序的呢?
带着这样的疑问,我自己写了一个很简单的例子,然后进行调试,跟进源码,终于发现的一点端倪,然后把我自己理解分享给大家
关于如何进入源码,以及查看源码中与变量有关的信息我的有一篇博客有些到,有时间可以去看下。。
接下来上代码://Test类 package TEST;
public class Test implements Comparable
{
int age = 0;
String Name;
public Test(int age, String name)
{
super();
this.age = age;
Name = name;
}
public int compareTo(Test o)
{
if (this.age > o.age)
return 1;
else return -1;
}
public String toString()
{
return age+Name;
}
}
//TestDemo类
package TEST;
import java.util.Arrays;
public class TestDemo
{
public static void main(String args[])
{
Test t[] = {new Test(6, "paul"), new Test(5, "ss"), new Test(2, "kk"),
new Test(3, "tt"), new Test(1, "ii")};
Arrays.sort(t);
for (int i=0; i
接下来我们加入断点进行调试
首先我们进入方法1: Arrays.class中的sort()方法
public static void sort(Object[] a)
{
if (LegacyMergeSort.userRequested)
legacyMergeSort(a);
Else
ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}
接下来进入方法2:ComparableTimSort.class 的sort()方法
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;
}
下一步进入:方法3:ComparableTimSort.class的countRunAndMakeAscending方法里面终于见到了我们的CompareTo方法
private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {
assert lo < hi;
int runHi = lo + 1;
if (runHi == hi)
return 1;
// Find end of run, and reverse range if descending
if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) {
while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)
runHi++;
//反转对象数组的lo~runHi-1部分,该方法也在ComparableTimSort.class中
reverseRange(a, lo, runHi);
} else {
while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0)
runHi++;
}
return runHi - lo;
}
这里你应该终于明白了为什么一定要实现comparable接口中的compareTo方法了吧,如何你没有实现,那么通过接口去找compareTo方法肯定会报错啊(因为这时候找不到compareTo方法,未定义)。。。。。。。。。
结合Test类中的compareTo方法,当this.age 所以进入while循环,直到 a[runHi]).compareTo(a[runHi - 1])>=0时结束循环,后反转对象数组的lo~runHi-1部分 (ComparableTimSort.class)reverseRange(a, lo, runHi)的源码如下: private static void reverseRange(Object[] a, int lo, int hi) { 我花了一点时间看了一下,就是C语言数据结构的二分排序法(也叫折半插入法,它是插入排序的一种改进版) 基本思想是:现将部分数组的部分元素变成一个有序的数组,后面的元素通过折半插入将它变成一个有序的数组。 例如前文:数组变成了2,5,6,3,1 则3插入前面有序的数组中,变成了 2,3,5,6,1 1在插入前面有序的数组中,变成了1,2,3,5,6 大家有时间可以去研究下。。这里不做详细说明。。到了这里方法基本都跟进并介绍完了 输出结果: 1ii 那如何是降序呢? 修改Test类中的compareTo方法: public int compareTo(Test o) { 将返回值调换就行了,输出结果: 6paul 关于compareTo方法的实现及返回值还有一下的组合: 譬如 public int compareTo(Test o) { 以及:public int compareTo(Test o) { 那他们到底是升序还是降序呢?自己结合源码可以去思考一下。 但是上面两种不建议写,因为容易混淆。推荐写最上面两种。。。 写了这么多 总结一下: public int compareTo(Test o) { public int compareTo(Test o) { 我自己记忆的方法是: 大于号 返回1,正乘正为正,所以升序(可以把>号想象为正) 大于号返回-1,正乘负为负,所以降序 提示非常重要的一点,compareTo中的方法一定要有至少两个以上(其实两个足够)的返回值,而且一个返回值一定要小于0,另一个一定要大于或等于0。 否则排序不会成功。自己结合ComparableTimSort.class的countRunAndMakeAscending方法分析。 最后留一个问题:我们说可以按照年龄属性进行降序升序排序,但比如有如下要求。 要求按照年龄大小升序排列,当年龄相同时,按照name属性降序排列,这时候compareTo函数怎么写呢? 大家可以去思考一下,这里我就不贴代码出来了,相信大家看过前文,自己思考一下应该可以写出来。 好了,这个博客写了好多小时了,该结束了,现在是北京时间10:25。撤了,撤了,大家晚安。 2017/6/19/ 10:25 ---祥子
hi--;
while (lo < hi) {
Object t = a[lo];
a[lo++] = a[hi];
a[hi--] = t;
}
}此时经过反转后数组变为了 2, 5, 6, 3, 1(这里没有写name属性),也就是前面3个对象是有序的,升序接下来我们就进入了下一个方法(ComparableTimSort.class) 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;
/* Invariants: * pivot >= all in [lo, left). * pivot < all in [right, start). */
while (left < right)
{
int mid = (left + right) >>> 1;
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;
}
}
2kk
3tt
5ss
6paul 升序。
if (this.age > o.age){
return -1;
}
else{
return 1;
}
}
5ss
3tt
2kk
1ii 降序
if (o.age > this.age){
return 1;
}
else{
return -1;
}
}
if (o.age > this.age){
return -1;
}
else{
return 1;
}
}
if (this.age > o.age){
return 1;
}
else{
return -1;
}
}升序
if (this.age > o.age){
return -1;
}
else{
return 1;
}
}降序