简介:java的排序可以通过Collections.sort(LIst)和Arrays.sort(Array)进行实现,总的看来大体的实现方式有两种,一种是排序对象自身实现Comparable接口, 另外一种就是在使用sort方法是传入第二个参数Comparator.

跟c,c++中的cmp函数一样, java也需要方法来对比两个对象的大小,cmp的实现由两种方式,一种实现Comparable接口的类,通过实现compareTo方法来对比对象的大小。第二种方法Comparator接口中通过实现compare方法来满足该要求。 在排序过程中equals方法不是必须的。

一、用Comparable接口实现排序

import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class User implements Comparable<Object>{

	private static Collator cmp = Collator.getInstance();
	
	private String username;
	
	private String password;
	
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public User(String username, String password) {
		this.username = username;
		this.password = password;
	}

	@Override
	public int compareTo(Object o) {
		User u = (User)o;
		return cmp.compare(this.username, u.getUsername());
	}
	public static void main(String[] args) {
		List<User> us = new ArrayList<User>();
		us.add(new User("chuninsane", "123"));
		us.add(new User("boas", "123"));
		us.add(new User("gg", "123"));
		Collections.sort(us);
		for(User u: us) {
			System.out.println(u.getUsername());
		}
	}
}



上面的compareTo方法中使用Collator中的compare来对User的username进行比较, 最后比较的结果是按照username的字典序进行排序

现在来分析一下Collections.sort方法的源码

public static <T extends Comparable<? super T>> void sort(List<T> list) {
        Object[] a = list.toArray();
        Arrays.sort(a);
        ListIterator<T> i = list.listIterator();
        for (int j=0; j<a.length; j++) {
            i.next();
            i.set((T)a[j]);
        }
    }

从上面可以清楚看出,Collections.sort方法也调用了Arrays.sort方法, 将list先转换为Array,然后排序结束之后再将数据复制回list中

下面我们接着分析Arrays.sort方法

public static void sort(Object[] a) {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a);
        else
            ComparableTimSort.sort(a);
    }

LegacyMergeSort.userRequested参数决定是采用MergeSort还是使用TimeSort来进行排序


LegacyMergeSort.userRequested由"java.util.Arrays.useLegacyMergeSort"方法进行设置

 legacyMergeSort方法的源码


private static void legacyMergeSort(Object[] a) {
        Object[] aux = a.clone();
        mergeSort(aux, a, 0, a.length, 0);
    }

legacyMergeSort又调用merge进行排序

private static void mergeSort(Object[] src,
                                  Object[] dest,
                                  int low,
                                  int high,
                                  int off) {
        int length = high - low;

        if (length < INSERTIONSORT_THRESHOLD) {
            for (int i=low; i<high; i++)
                for (int j=i; j>low &&
                         ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
                    swap(dest, j, j-1);
            return;
        }

        int destLow  = low;
        int destHigh = high;
        low  += off;
        high += off;
        int mid = (low + high) >>> 1;
        mergeSort(dest, src, low, mid, -off);
        mergeSort(dest, src, mid, high, -off);
    
        if ((<span style="color:#ff0000;">(Comparable)src[mid-1]).compareTo(src[mid])</span> <= 0) {
            System.arraycopy(src, low, dest, destLow, length);
            return;
        }

        for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
            if (q >= high || p < mid && (<span style="color:#ff0000;">(Comparable)src[p]).compareTo(src[q])</span><=0)
                dest[i] = src[p++];
            else
                dest[i] = src[q++];
        }
    }

顾名思义, mergeSort就是归并排序,将dest分成

INSERTIONSORT_THRESHOLD大小的多个子串, 然后采用选择排序对子串进行排序,最后进行整理

上面代码中需要注意的就是((Comparable)src[mid-1]).compareTo(src[mid]) <= 0, 表示两个子串可以直接连接起来。否则就采用归并排序的合并方式进行合并。

②ComparableTimSort.sort方法的源码

static void sort(Object[] a) {
          sort(a, 0, a.length);
    }

    static void sort(Object[] a, int lo, int hi) {
        rangeCheck(a.length, lo, hi);
        int nRemaining  = hi - lo;
        if (nRemaining < 2)
            return;  

        if (nRemaining < MIN_MERGE) {
            int initRunLen = countRunAndMakeAscending(a, lo, hi);
            binarySort(a, lo, hi, lo + initRunLen);
            return;
        }

        ComparableTimSort ts = new ComparableTimSort(a);
        int minRun = minRunLength(nRemaining);
        do {
            int runLen = countRunAndMakeAscending(a, lo, hi);

            if (runLen < minRun) {
                int force = nRemaining <= minRun ? nRemaining : minRun;
                binarySort(a, lo, lo + force, lo + runLen);
                runLen = force;
            }

            ts.pushRun(lo, runLen);
            ts.mergeCollapse();

            lo += runLen;
            nRemaining -= runLen;
        } while (nRemaining != 0);

        assert lo == hi;
        ts.mergeForceCollapse();
        assert ts.stackSize == 1;
    }

TimSort是一个归并排序做了大量优化的排序方式


此方法中传入的带排序数组长度若小于阀值MIN_MERGE,则调用binarySort方法, 然后开始真正的TimSort方法的处理(待补充)


二、用Comparator实现排序

jdk中对该接口的描述:

强行对某个对象 collection 进行整体排序 的比较函数。可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 
提供排序。

在Set和Map中Comparator的用法

Comparator的定义

import java.text.Collator;
import java.util.Comparator;

public class UserComparator implements Comparator<User> {

	Collator cmp = Collator.getInstance();
	
	@Override
	public int compare(User o1, User o2) {
		return cmp.compare(o1.getUsername(), o2.getUsername());
	}
}

使用Comparator进行排序

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class User {
	private String username;
	
	private String password;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
	public User(String username, String password) {
		this.username = username;
		this.password = password;
	}
	public static void main(String[] args) {
		List<User> us = new ArrayList<User>();
		us.add(new User("aaa", "123"));
		us.add(new User("bbb", "123"));
		us.add(new User("aaa", "123"));
		Collections.sort(us, new UserComparator());
		for(User u: us) {
			System.out.println(u.getUsername());
		}
	}
	
}



首先还是查看Collections.sort的源码

public static <T> void sort(List<T> list, Comparator<? super T> c) {
        Object[] a = list.toArray();
        Arrays.sort(a, (Comparator)c);
        ListIterator i = list.listIterator();
        for (int j=0; j<a.length; j++) {
            i.next();
            i.set(a[j]);
        }
    }

这里还是调用的Arrays.sort方法

public static <T> void sort(T[] a, Comparator<? super T> c) {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a, c);
        else
            TimSort.sort(a, c);
    }

上面的方法还是和Comparable中的类似

private static <T> void legacyMergeSort(T[] a, Comparator<? super T> c) {
        T[] aux = a.clone();
        if (c==null)
            mergeSort(aux, a, 0, a.length, 0);
        else
            mergeSort(aux, a, 0, a.length, 0, c);
    }

merge的源码

private static void mergeSort(Object[] src,
                                  Object[] dest,
                                  int low, int high, int off,
                                  Comparator c) {
        int length = high - low;

        if (length < INSERTIONSORT_THRESHOLD) {
            for (int i=low; i<high; i++)
                for (int j=i; j>low && <span style="color:#ff0000;">c.compare(dest[j-1], dest[j])</span>>0; j--)
                    swap(dest, j, j-1);
            return;
        }

        int destLow  = low;
        int destHigh = high;
        low  += off;
        high += off;
        int mid = (low + high) >>> 1;
        mergeSort(dest, src, low, mid, -off, c);
        mergeSort(dest, src, mid, high, -off, c);

        if (c.compare(src[mid-1], src[mid]) <= 0) {
           System.arraycopy(src, low, dest, destLow, length);
           return;
        }

        for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
            if (q >= high || p < mid && c.compare(src[p], src[q]) <= 0)
                dest[i] = src[p++];
            else
                dest[i] = src[q++];
        }
    }



从上面的merge可以看出Comparable个Comparator的最大区别就是在进行对象对比时用的方法不同, Comparable中将对象强制转换为Comparable类型,如果你的类没实现Comparable接口则会报

java.lang.ClassCastException XXXDO cannot be cast to java.lang.Comparable错误, Comparator则中则是调用传入的Comparator中的compare方法来比较两个对象的大小。

——结束