java 集合中的Collections.sort()排序方法的源码分析(一)

Collections.sort()方法 是用来对 List 进行排序的,主要有两种方式。

  1、List 中的对象继承Comparable接口,并实现 接口中的 compareTo 方法

   2、Collections.sort 方法重载 

现在先讨论下第一种方法。

      List 对象为什么要实现compareTo 方法呢?这种排序方式的关键步骤有哪些 ?这就是我今天写代码时所纠结的地方。下面,以网上找的一段代码为切入点,探讨一下这种排序方式的实现原理:

//先定义需要比较的对象类

public class PersonH implements Comparable<PersonH>
{

	
	private int level;
	
	public Integer getLevel()
	{
		return level;
	}
	public void setLevel(Integer level1)
	{
		this.level = level1;
	}
	<span style="color:#3333FF;">@Override
	public int compareTo(PersonH o)
	{
		// TODO Auto-generated method stub
	  return this.getLevel().compareTo(o.getLevel());		
	}	</span>
}

//测试类

public class Main
{

	/**
	 * @param args
	 */
	public static void main(String[] args)
	{
		// TODO Auto-generated method stub
		PersonH person  = new PersonH();
		person.setLevel(6);
		PersonH person2 = new PersonH();
		person2.setLevel(8);
		PersonH person3 = new PersonH();
		person3.setLevel(3);
		ArrayList<PersonH> personList = new ArrayList<PersonH>();
		
		personList.add(person);
		personList.add(person2);
		personList.add(person3);
		
		<span style="color:#3333FF;">Collections.sort(personList);</span>//排序
		for(PersonH personH : personList) //输出排序后结果
		{
			System.out.println(personH.getLevel());
		}
	}


  是一段比较典型的List 中的对象继承Comparable接口 进而进行排序的方法。

 要注意,这个对象类实现了Comparable接口,点开Comparable接口

public interface Comparable<T> {
    /**
     * Compares this object with the specified object for order.  Returns 
     *         from being compared to this object.
      。。。。这里有一堆
     */
    public int compareTo(T o);
}

会发现,这个接口就一个compareTo 方法。

再看 测试类, 这里将比较类的对象加到了一个personList中,并作为Collections.sort(personList)的参数。

这个比较方法 就这么一个参数,具体排序功能是怎么实现的呢?点开源码查看sort方法。

public static void sort(EList<?> list)
  {
    Object[] listAsArray = list.toArray();
    Arrays.sort(listAsArray);   
    for (int i=0; i < listAsArray.length; i++)
    {
      int oldIndex = indexOf(list, listAsArray[i], i);
      if (i != oldIndex)
      {
        list.move(i, oldIndex);
      }
    }    
  }

这里先把list转化为对象数组,然后调用Arrays.sort(listAsArray)中的sort方法。

然后看这里的sort()方法有何不同。

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

//① clone方法(如果我们要用aux保存a对象的数据,但又不希望aux对象数据被改变时不影响到a。实现clone()方法是其一种最简单,也是最高效的手段。

// ② 这里的sort()方法参数是Object[]数组。

接着看mergeSort():

/**
     * Src is the source array that starts at index 0
     * Dest is the (possibly larger) array destination with a possible offset
     * low is the index in dest to start sorting
     * high is the end index in dest to end sorting
     * off is the offset to generate corresponding low, high in src
     */

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

	// Insertion sort on smallest arrays
        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 list is already sorted, just copy from src to dest.  This is an
        // optimization that results in faster sorts for nearly ordered lists.
        if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) {
            System.arraycopy(src, low, dest, destLow, length);
            return;
        }

        // Merge sorted halves (now in src) into dest
        for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
            if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0)
                dest[i] = src[p++];
            else
                dest[i] = src[q++];
        }
    }

我们将代码分段来看:

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

	// Insertion sort on smallest arrays
        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;
         }

 输入参数dest[ ] 就是原来那个对象数组,对象重写的那个compareTo方法就用上了,写到这里就能解释为什么对象数组要将compareTo方法重写了,这就解决了第一个问题。

这一步(Comparable) dest[j-1]).compareTo(dest[j])>0;j–) 中强转成 Comparable 类型什么意思呢?

       ((Comparable)dest[j-1]) 这样,先强转成Comparable类型,然后才能再用comparaTo方法。

这里有个常量INSERTIONSORT_THRESHOLD ,为什么要有这么一个常量呢?

/**
     * Tuning parameter: list size at or below which insertion sort will be
     * used in preference to mergesort or quicksort.
     */
    private static final int INSERTIONSORT_THRESHOLD = 7;

当长度少于7的时候,使用直接插入排序。插入排序适用于小数列的排序,提高了程序运行的效率。

当长度大于7的时候,程序先将数组拆分成低区间和高区间两个区间,再调用两个递归对这两个区间元素进行排序。在递归时还得推断已经划分的区间元素是否还多于7位,假设多于7位的话继续划分成两个区间,这样循环递归调用。

// Recursively sort halves of dest into src
        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);

第6行为什么用>>>呢?(知乎上这里说的很清楚http://www.zhihu.com/question/20101702

这里开始递归排序,我们不需要关注off变量,这个变量是排序数组中部分区域的时候使用的,而我们要排序的是整个数组

// If list is already sorted, just copy from src to dest.  This is an
        // optimization that results in faster sorts for nearly ordered lists.
        if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) {
            System.arraycopy(src, low, dest, destLow, length);
            return;
        }

左边和右边排好序之后,开始合并。这时src[low ~ mid – 1]和src[mid ~ high – 1]都是有序的,这时比较src[mid – 1]和src[mid],如果前者比后者小,那么整个src数组就是有序的了,只需将其复制到目标数组后,就完成了排序,这种碰运气的做法感觉并没有什么卵用,这种情况太少啦!

 

      // Merge sorted halves (now in src) into dest
        for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
            if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0)
                dest[i] = src[p++];
            else
                dest[i] = src[q++];
        }

这里就是传统的合并操作了。两个排好序的数组不断进行比较,将比较好的元素从小到大加到另一个数组中。

回过头来看第三段代码,这段代码执行完,listAsArry 就成了一个有序的对象数组了。接着进入到 for 循环里,这个for 循环有什么作用呢?

public static void sort(EList<?> list)
  {
    Object[] listAsArray = list.toArray();
    Arrays.sort(listAsArray);   
    for (int i=0; i < listAsArray.length; i++)
    {
      int oldIndex = indexOf(list, listAsArray[i], i);
      if (i != oldIndex)
      {
        list.move(i, oldIndex);
      }
    }    
  }

查看IndexOf()方法:

大概读了一下,这个方法作用是在list中从角标fromIndex开始查找对象o所在的位置,不存在返回 -1.

public static int indexOf(List<?> list, Object o, int fromIndex)
  {
    if (fromIndex < 0)
    {
      fromIndex = 0;
    }


    int size = list.size();
    for (int i = fromIndex; i < size; i++)
    {
      Object element = list.get(i);
      if (o == null)
      {
        if (element == null)
        {
          return i;
        }
      }
      else if (o == element || o.equals(element))
      {
        return i;
      }
    }
    return -1;
  }

这个方法放到这段代码中的含义就是比较同一个元素在 list 中与排序好的 listAsArray 中的角标是否相同。如果不同,则调用list.move()方法。

 /**
   * Moves the object from the old position to the new position.
   * @param newPosition the position of the object after the move.
   * @param oldPosition the position of the object before the move.
   * @return the moved object.
   */
  E move(int newPosition, int oldPosition);


可以看出这个move()的作用是,将list 中的某个对象从旧位置移到新位置。 放到这段代码中,move的作用就是将list 中的元素按照 listAsArray 中元素的位置顺序进行排序。得到的 List  就是sort() 方法输入的并且已经排好序的 list 。

综上,看出这个排序方式的主要实现步骤有三个:

1、将输入参数 List 转换为一个对象数组。

2、 将对象数组传给Arrays类的sort方法(实际上这个排序本质上是调用Arrays.sort(),这也体现了面向对象的代码复用这一特性)。

3、排序完成后,调用list.move() 方法将list中的元素按照数组中的元素顺序进行排序。

    个人认为实际应用的时候只要记住用法就好了,可以深究,但是真的不必每一个点都去深究,东西太多,而时间又太少。。。

 

 

    原文作者:java集合源码分析
    原文地址: https://blog.csdn.net/SouL_EateL/article/details/49129465
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞