Array.sort详解(jdk6)

java.util.Arrays提供了对数组int[] long[] byte[] char[] short[] double[] float[] Object[]的排序算法Arrays.sort(T[]),以及更高级的Arrays.sort(T[], Comparator<? super T> c);

先看对int\long\byte\char\short的排序算法sort1(byte x[], int off, int len)

1)排序长度len<7时,直接采用插入排序,是因为虽然复杂度为O(n^2),但是由于个数较少,插入排序可以省掉快排递归需要的时间,效率较高;

1   // Insertion sort on smallest arrays
2     if (len < 7) {
3         for (int i=off; i<len+off; i++)
4         for (int j=i; j>off && x[j-1]>x[j]; j--)
5             swap(x, j, j-1);
6         return;
7     }

2)len>=7时,采用快排递归进行排序;为了优化效率,需要选取较好的比较值来把排序数组尽量分为长度相近的两部分;选择比较值的方法如下:

3)len==7时,取中点的值为比较值:

1 int m = off + (len >> 1);  

4)len>7 && len<=40时,取头、尾、中点三个值的中间值为比较值:

1   if (len > 7) {
2         int l = off;
3         int n = off + len - 1;
4         m = med3(x, l, m, n); // Mid-size, med of 3
5     }

  其中med3为取中间值:

1    /**
2      * Returns the index of the median of the three indexed bytes.
3      */
4     private static int med3(byte x[], int a, int b, int c) {
5     return (x[a] < x[b] ?
6         (x[b] < x[c] ? b : x[a] < x[c] ? c : a) :
7         (x[b] > x[c] ? b : x[a] > x[c] ? c : a));
8     }

5)len>40时,将数组8等分得到9个点,分三组取中间值之后将三个中间值的中间值作为比较值:

1     if (len > 40) {        // Big arrays, pseudomedian of 9
2         int s = len/8;
3         l = med3(x, l,     l+s, l+2*s);
4         m = med3(x, m-s,   m,   m+s);
5         n = med3(x, n-2*s, n-s, n);
6         }
7         m = med3(x, l, m, n); // Mid-size, med of 3

6)随后对数组进行快排,其中优化的部分是将与比较值相等的值放到数组头和尾,随后进行swap将这些相等的值放到比较值所在相同的位置,这样比快排要省一些比较时间;

 1        byte v = x[m];
 2
 3     // Establish Invariant: v* (<v)* (>v)* v*
 4     int a = off, b = a, c = off + len - 1, d = c;
 5     while(true) {
 6         while (b <= c && x[b] <= v) {
 7         if (x[b] == v)
 8             swap(x, a++, b);
 9         b++;
10         }
11         while (c >= b && x[c] >= v) {
12         if (x[c] == v)
13             swap(x, c, d--);
14         c--;
15         }
16         if (b > c)
17         break;
18         swap(x, b++, c--);
19     }
20
21     // Swap partition elements back to middle
22     int s, n = off + len;
23     s = Math.min(a-off, b-a  );  vecswap(x, off, b-s, s);
24     s = Math.min(d-c,   n-d-1);  vecswap(x, b,   n-s, s);
25
26     // Recursively sort non-partition-elements
27     if ((s = b-a) > 1)
28         sort1(x, off, s);
29     if ((s = d-c) > 1)
30         sort1(x, n-s, s);    

  其中vecswap是一个按顺序拷贝

1     /**
2      * Swaps x[a .. (a+n-1)] with x[b .. (b+n-1)].
3      */
4     private static void vecswap(byte x[], int a, int b, int n) {
5     for (int i=0; i<n; i++, a++, b++)
6         swap(x, a, b);
7     }

sort1对5种基本类型的排序到此结束,主要是采用了优化后的快排。

对double\float的排序是sort2(double a[], int fromIndex, int toIndex)

先看代码:

 1   private static void sort2(double a[], int fromIndex, int toIndex) {
 2         final long NEG_ZERO_BITS = Double.doubleToLongBits(-0.0d);
 3         /*
 4          * The sort is done in three phases to avoid the expense of using
 5          * NaN and -0.0 aware comparisons during the main sort.
 6          */
 7
 8         /*
 9          * Preprocessing phase:  Move any NaN‘s to end of array, count the
10          * number of -0.0‘s, and turn them into 0.0‘s.
11          */
12         int numNegZeros = 0;
13         int i = fromIndex, n = toIndex;
14         while(i < n) {
15             if (a[i] != a[i]) {
16         double swap = a[i];
17                 a[i] = a[--n];
18                 a[n] = swap;
19             } else {
20                 if (a[i]==0 && Double.doubleToLongBits(a[i])==NEG_ZERO_BITS) {
21                     a[i] = 0.0d;
22                     numNegZeros++;
23                 }
24                 i++;
25             }
26         }
27
28         // Main sort phase: quicksort everything but the NaN‘s
29     sort1(a, fromIndex, n-fromIndex);
30
31         // Postprocessing phase: change 0.0‘s to -0.0‘s as required
32         if (numNegZeros != 0) {
33             int j = binarySearch0(a, fromIndex, n, 0.0d); // posn of ANY zero
34             do {
35                 j--;
36             } while (j>=0 && a[j]==0.0d);
37
38             // j is now one less than the index of the FIRST zero
39             for (int k=0; k<numNegZeros; k++)
40                 a[++j] = -0.0d;
41         }
42     }

核心思想仍然是采用优化后的快排(29行sort1),但对double和float的性质做了一些修改;

由于double中0和-0.0d并不相等,因此在12-26行将等于0和NaN的值放在数组尾部不参与排序;注意15行a[i]!=a[i]是检验double/float中NaN的常用方式,当d是NaN时,d==d为false,而d!=d为true;

随后对非0值的数组进行优化后的快排,32-41行将未参与排序的值放入排序后的队尾;

对Object[]的排序sort(Object[] a)

与上述基本类型不同,对对象数组进行排序采用的是mergeSort,原因是mergeSort稳定性比快排好,Object中除了排序项以外存在其他成员,因此对稳定性要求高;

 1     /**
 2      * Src is the source array that starts at index 0
 3      * Dest is the (possibly larger) array destination with a possible offset
 4      * low is the index in dest to start sorting
 5      * high is the end index in dest to end sorting
 6      * off is the offset to generate corresponding low, high in src
 7      */
 8     private static void mergeSort(Object[] src,
 9                   Object[] dest,
10                   int low,
11                   int high,
12                   int off) {
13     int length = high - low;
14
15     // Insertion sort on smallest arrays
16         if (length < INSERTIONSORT_THRESHOLD) {
17             for (int i=low; i<high; i++)
18                 for (int j=i; j>low &&
19              ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
20                     swap(dest, j, j-1);
21             return;
22         }
23
24         // Recursively sort halves of dest into src
25         int destLow  = low;
26         int destHigh = high;
27         low  += off;
28         high += off;
29         int mid = (low + high) >>> 1;
30         mergeSort(dest, src, low, mid, -off);
31         mergeSort(dest, src, mid, high, -off);
32
33         // If list is already sorted, just copy from src to dest.  This is an
34         // optimization that results in faster sorts for nearly ordered lists.
35         if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) {
36             System.arraycopy(src, low, dest, destLow, length);
37             return;
38         }
39
40         // Merge sorted halves (now in src) into dest
41         for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
42             if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0)
43                 dest[i] = src[p++];
44             else
45                 dest[i] = src[q++];
46         }
47     }

  当元素个数少于7时,仍然采用插入排序,多于7时采用递归的mergeSort;

若自己 实现了Comparator<? super T>,则上述算法的Comparable采用c比较即可;

thanks to

http://www.cnblogs.com/gw811/archive/2012/10/04/2711746.html

时间: 2024-11-05 15:54:02

Array.sort详解(jdk6)的相关文章

数据结构 - 简单选择排序(simple selection sort) 详解 及 代码(C++)

数据结构 - 简单选择排序(simple selection sort) 本文地址: http://blog.csdn.net/caroline_wendy/article/details/28601965 选择排序(selection sort) : 每一趟在n-i+1个记录中选取关键字最小的记录作为有序序列中第i个记录. 简单选择排序(simple selection sort) : 通过n-i次关键字之间的比较, 从n-i+1个记录中选出关键字最小的记录, 并和第i个记录交换. 选择排序需

数据结构 - 归并排序(merging sort) 详解 及 代码

归并排序(merging sort) 详解 及 代码 本文地址: http://blog.csdn.net/caroline_wendy 归并排序(merging sort): 包含2-路归并排序, 把数组拆分成两段, 使用递归, 将两个有序表合成一个新的有序表. 归并排序(merge sort)的时间复杂度是O(nlogn), 实际效果不如快速排序(quick sort)和堆排序(heap sort), 但是归并排序是稳定排序, 而快速排序和堆排序则不是. 代码: /* * main.cpp

设计模式 - 模板方法模式(template method pattern) 排序(sort) 详解

模板方法模式(template method pattern) 排序(sort) 详解 本文地址: http://blog.csdn.net/caroline_wendy 参考模板方法模式(template method pattern): http://blog.csdn.net/caroline_wendy/article/details/32159455 模板方法模式的一个主要的应用是排序(sort)算法. 对象的排列方式并不是完全相同, 所以需要排序(sort)算法compareTo()

数据结构 - 堆排序(heap sort) 详解 及 代码(C++)

堆排序(heap sort) 详解 及 代码(C++) 本文地址: http://blog.csdn.net/caroline_wendy 堆排序包含两个步骤: 第一步: 是建立大顶堆(从大到小排序)或小顶堆(从小到大排序), 从下往上建立; 如建堆时, s是从大到小; 第二步: 是依次交换堆顶和堆底, 并把交换后的堆底输出, 只排列剩余的堆, 从上往下建立; 如构造时, s始终是1; 代码: /* * main.cpp * * Created on: 2014.6.12 * Author: S

JavaScript中数组Array方法详解

ECMAScript 3在Array.prototype中定义了一些很有用的操作数组的函数,这意味着这些函数作为任何数组的方法都是可用的. 1.Array.join()方法 Array.join()方法将数组中所有元素都转化为字符串并连接在一起,返回最后生成的字符串.可以指定一个可选的符号或字符串在生成的字符串中来分隔数组的各个元素.如果不指定分隔符,默认使用逗号.注意:此方法不会改变原始数组 var arr = ['a', 'b', 'c']; console.log(arr.join());

原生JS:Array对象详解

Array对象 创建数组: 数组字面量:var arr1 = [1,2,3]; Array构造函数1:var arr2 = new Array(1,2,3);  //[1,2,3] Array构造函数2:var arr3 = new Array(3);  var arr3 = new Array(‘3’);  var arr3 = new Array('ol'); 错误写法:var arr3 = new Array(3.2);  var arr3 = new Array(-3); 例:var a

JavaScript中的Array数组详解

ECMAScript中的数组与其他多数语言中的数组有着相当大的区别,虽然数组都是数据的有序列表,但是与其他语言不同的是,ECMAScript数组的每一项可以保存任何类型的数据.也就是说,可以用数组的第一个位置来保存字符串,第二个位置保存数值,第三个位置保存对象,而且ECMAScript数组的大小是可以动态调整的,即可以随着数据的添加自动增长以容纳新增数据. 数组的创建 创建数组的基本方式有两种,第一种是使用Array构造函数. var arr = new Array(); 如果预先知道数组要保存

javascript Array数组详解 各种方法

1.数组的声明方法(1): arrayObj = new Array(); //创建一个数组.复制代码 代码如下: var arr1 = new Array(); (2):arrayObj = new Array([size]) 创建一个数组并指定长度,注意不是上限,是长度.复制代码 代码如下: var a = new Array(5); (3):arrayObj = new Array([element0[, element1[, ...[, elementN]]]]) 创建一个数组并赋值.复

mapreduce shuffle 和sort 详解

    MapReduce 框架的核心步骤主要分两部分:Map 和Reduce.当你向MapReduce 框架提交一个计算作业时,它会首先把计算作业拆分成若干个Map 任务,然后分配到不同的节点上去执行,每一个Map 任务处理输入数据中的一部分,当Map 任务完成后,它会生成一些中间文件,这些中间文件将会作为Reduce 任务的输入数据.Reduce 任务的主要目标就是把前面若干个Map 的输出汇总到一起并输出. 本文的重点是剖析MapReduce 的核心过程--Shuffle和Sort.在本文