Java中Arrays.sort自定义一维数组、二维数组的排序方式
作者:你的代码没bug
Java Arrays.sort自定义一维数组、二维数组的排序
一维数组
自定义排序方法:
//降序 //形式一: Arrays.sort(arr, new Comparator<Integer>() { public int compare(Integer a, Integer b) { return b-a; } }); //形式二: Arrays.sort(arr, (a, b) -> (b - a));
问题:
可以试着把arr赋值为以下值:
也就是int的边界值
Integer[] arr = {-2147483648, 1, 2147483647};
代码:
Integer[] arr = {-2147483648, 1, 2147483647}; Arrays.sort(arr, new Comparator<Integer>() { public int compare(Integer a, Integer b) { return b-a; } }); for(Integer item : arr) { System.out.print(item+" "); }
结果:并不是降序
-2147483648 2147483647 1
这是因为自定义排序内是相减的操作,int边界上的值相加减可不一定在int范围内。
解决:
Arrays.sort(arr, Comparator.reverseOrder()); 或 Arrays.sort(arr, (a, b) -> Integer.compare(b, a));
两种方式都可以 解决 上述出现的问题
Integer[] arr = {-2147483648, 1, 2147483647}; Arrays.sort(arr, Comparator.reverseOrder()); //Arrays.sort(arr, (a, b) -> Integer.compare(b, a)); for(Integer item : arr) { System.out.print(item+" "); }
结果:
2147483647 1 -2147483648
二维数组
int[][] people = {{7,0},{4,4},{7,1},{5,0},{6,1},{5,2}}; Arrays.sort(people, (a, b) -> { if (a[0] == b[0]) return a[1] - b[1]; return b[0] - a[0]; }); for(int[] a : people) { System.out.println(a[0]+" "+a[1]); }
很容易得出上述代码的功能,根据二维数组的第一个元素降序排序,如果第一个元素相同,则按照第二个元素升序。
结果:
7 0
7 1
6 1
5 0
5 2
4 4
当然相减的排序方式难免不适用于边界数据,只要采用上述一维数组那样的方式就好了。
形式:
Arrays.sort(points, (o1, o2) -> Integer.compare(o1[0], o2[0]));
那么就可以改写为:
Arrays.sort(people, (o1, o2) -> { if(o1[0] == o2[0])return Integer.compare(o1[1], o2[1]); return Integer.compare(o2[0], o1[0]); });
理解Java 中的Arrays.sort()方法
Java的Arrays类中有一个sort()方法,该方法是Arrays类的静态方法,在需要对数组进行排序时,非常的好用。
但是sort()的参数有好几种,基本上是大同小异,下面是以int型数组为例的Arrays.sort()的典型用法
import java.util.Arrays; import java.util.Comparator; /** * Arrays.sort()排序 */ public class SortTest { public static void main(String []args) { int[] ints=new int[]{2,324,4,57,1}; System.out.println("增序排序后顺序"); Arrays.sort(ints); for (int i=0;i<ints.length;i++) { System.out.print(ints[i]+" "); } System.out.println("\n减序排序后顺序"); //要实现减序排序,得通过包装类型数组,基本类型数组是不行滴 Integer[] integers=new Integer[]{2,324,4,4,6,1}; Arrays.sort(integers, new Comparator<Integer>() { /* * 此处与c++的比较函数构成不一致 * c++返回bool型,而Java返回的为int型 * 当返回值>0时 * 进行交换,即排序(源码实现为两枢轴快速排序) */ public int compare(Integer o1, Integer o2) { return o2-o1; } public boolean equals(Object obj) { return false; } }); for (Integer integer:integers) { System.out.print(integer+" "); } System.out.println("\n对部分排序后顺序"); int[] ints2=new int[]{212,43,2,324,4,4,57,1}; //对数组的[2,6)位进行排序 Arrays.sort(ints2,2,6); for (int i=0;i<ints2.length;i++) { System.out.print(ints2[i]+" "); } } }
排序结果如下
增序排序后顺序
1 2 4 57 324
减序排序后顺序
324 6 4 4 2 1
对部分排序后顺序
212 43 2 4 4 324 57 1
打开Arrays.sort()源码,还是以int型为例,其他类型也是大同小异
public static void sort(int[] a) { DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0); } public static void sort(int[] a, int fromIndex, int toIndex) { rangeCheck(a.length, fromIndex, toIndex); DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0); }
从源码中发现,两种参数类型的sort方法都调用了 DualPivotQuicksort.sort()方法继续跟踪源码
static void sort(int[] a, int left, int right, int[] work, int workBase, int workLen) { // Use Quicksort on small arrays if (right - left < QUICKSORT_THRESHOLD) { sort(a, left, right, true); return; } /* * Index run[i] is the start of i-th run * (ascending or descending sequence). */ int[] run = new int[MAX_RUN_COUNT + 1]; int count = 0; run[0] = left; // Check if the array is nearly sorted for (int k = left; k < right; run[count] = k) { if (a[k] < a[k + 1]) { // ascending while (++k <= right && a[k - 1] <= a[k]); } else if (a[k] > a[k + 1]) { // descending while (++k <= right && a[k - 1] >= a[k]); for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) { int t = a[lo]; a[lo] = a[hi]; a[hi] = t; } } else { // equal for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) { if (--m == 0) { sort(a, left, right, true); return; } } } /* * The array is not highly structured, * use Quicksort instead of merge sort. */ if (++count == MAX_RUN_COUNT) { sort(a, left, right, true); return; } } // Check special cases // Implementation note: variable "right" is increased by 1. if (run[count] == right++) { // The last run contains one element run[++count] = right; } else if (count == 1) { // The array is already sorted return; } // Determine alternation base for merge byte odd = 0; for (int n = 1; (n <<= 1) < count; odd ^= 1); // Use or create temporary array b for merging int[] b; // temp array; alternates with a int ao, bo; // array offsets from 'left' int blen = right - left; // space needed for b if (work == null || workLen < blen || workBase + blen > work.length) { work = new int[blen]; workBase = 0; } if (odd == 0) { System.arraycopy(a, left, work, workBase, blen); b = a; bo = 0; a = work; ao = workBase - left; } else { b = work; ao = 0; bo = workBase - left; } // Merging for (int last; count > 1; count = last) { for (int k = (last = 0) + 2; k <= count; k += 2) { int hi = run[k], mi = run[k - 1]; for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) { if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) { b[i + bo] = a[p++ + ao]; } else { b[i + bo] = a[q++ + ao]; } } run[++last] = hi; } if ((count & 1) != 0) { for (int i = right, lo = run[count - 1]; --i >= lo; b[i + bo] = a[i + ao] ); run[++last] = right; } int[] t = a; a = b; b = t; int o = ao; ao = bo; bo = o; } }
结合文档以及源代码,我们发现,jdk中的Arrays.sort()的实现是通过所谓的双轴快排的算法
/** * This class implements the Dual-Pivot Quicksort algorithm by * Vladimir Yaroslavskiy, Jon Bentley, and Josh Bloch. The algorithm * offers O(n log(n)) performance on many data sets that cause other * quicksorts to degrade to quadratic performance, and is typically * faster than traditional (one-pivot) Quicksort implementations. * * All exposed methods are package-private, designed to be invoked * from public methods (in class Arrays) after performing any * necessary array bounds checks and expanding parameters into the * required forms. * * @author Vladimir Yaroslavskiy * @author Jon Bentley * @author Josh Bloch * * @version 2011.02.11 m765.827.12i:5\7pm * @since 1.7 */
Java1.8的快排是一种双轴快排,顾名思义:双轴快排是基于两个轴来进行比较,跟普通的选择一个点来作为轴点的快排是有很大的区别的,双轴排序利用了区间相邻的特性,对原本的快排进行了效率上的提高,很大程度上是利用了数学的一些特性。。。。。嗯。。。反正很高深的样子
算法步骤
1.对于很小的数组(长度小于27),会使用插入排序。
2.选择两个点P1,P2作为轴心,比如我们可以使用第一个元素和最后一个元素。
3.P1必须比P2要小,否则将这两个元素交换,现在将整个数组分为四部分:
- (1)第一部分:比P1小的元素。
- (2)第二部分:比P1大但是比P2小的元素。
- (3)第三部分:比P2大的元素。
- (4)第四部分:尚未比较的部分。
- 在开始比较前,除了轴点,其余元素几乎都在第四部分,直到比较完之后第四部分没有元素。
4.从第四部分选出一个元素a[K],与两个轴心比较,然后放到第一二三部分中的一个。
5.移动L,K,G指向。
6.重复 4 5 步,直到第四部分没有元素。
7.将P1与第一部分的最后一个元素交换。将P2与第三部分的第一个元素交换。
8.递归的将第一二三部分排序。
疑问:为啥不用泛型
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。