最近在暑假实习,没什么任务给我做,不是我不能做,而是还没那资格,毕竟才来了一周多。闲着无事,在网上看看国内的牛公司的招聘要求,想自己能达到他们的要求,准备研究下JDK中的常用类的源代码。今天就来看看java.util.Arrays类。这个类是个数组工具类。主要提供方法sort(),fill(),binarySearch(),还有数组复制等方法。打开源文件,刚超过4千行,不过包括很多注释,那么我在这里主要讲讲这里面涉及的排序算法和查找算法。
一、binarySearch()方法,二分法查找算法,算法思想:当数据量很大适宜采用该方法。采用二分法查找时,数据需是排好序的。 基本思想:假设数据是按升序排序的,对于给定值x,从序列的中间位置开始比较,如果当前位置值等于x,则查找成功;若x小于当前位置值,则在数列的前半段中查找;若x大于当前位置值则在数列的后半段中继续查找,直到找到为止。
//针对int类型数组的二分法查找,key为要查找数的下标
private static int binarySearch0(int[] a, int fromIndex, int toIndex,
int key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) >>> 1;//无符号左移一位,相当于除以二
int midVal = a[mid];
if (midVal < key)
low = mid + 1;
else if (midVal > key)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found.
}
二、sort()方法针对引用类型数组采取的算法是归并排序。算法思想:归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
private static final int INSERTIONSORT_THRESHOLD = 7;//插入排序门槛
public static void sort(Object[] a) {
Object[] aux = (Object[])a.clone();
mergeSort(aux, a, 0, a.length, 0);
}
//归并排序
private static void mergeSort(Object[] src,
Object[] dest,
int low,
int high,
int off) {
int length = high - low;
if (length < INSERTIONSORT_THRESHOLD) { //若数组长度小于7,则用冒泡排序
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;
}
// 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);
// 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++];
}
}
三、sort()方法采取的是快速排序算法,算法思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
/**
* Swaps x[a] with x[b].
*/
private static void swap(int x[], int a, int b) {
int t = x[a];
x[a] = x[b];
x[b] = t;
}
public static void sort(int[] a) {
sort1(a, 0, a.length);
}
private static int med3(int x[], int a, int b, int c) {//找出三个中的中间值
return (x[a] < x[b] ?
(x[b] < x[c] ? b : x[a] < x[c] ? c : a) :
(x[b] > x[c] ? b : x[a] > x[c] ? c : a));
}
/**
* Sorts the specified sub-array of integers into ascending order.
*/
private static void sort1(int x[], int off, int len) {
// Insertion sort on smallest arrays
if (len < 7) {//采用冒泡排序
for (int i=off; i<len+off; i++)
for (int j=i; j>off && x[j-1]>x[j]; j--)
swap(x, j, j-1);
return;
}
//采用快速排序
// Choose a partition element, v
int m = off + (len >> 1); // Small arrays, middle element
if (len > 7) {
int l = off;
int n = off + len - 1;
if (len > 40) { // Big arrays, pseudomedian of 9
int s = len/8;
l = med3(x, l, l+s, l+2*s);
m = med3(x, m-s, m, m+s);
n = med3(x, n-2*s, n-s, n);
}
m = med3(x, l, m, n); // Mid-size, med of 3
}
int v = x[m];
// Establish Invariant: v* (<v)* (>v)* v*
int a = off, b = a, c = off + len - 1, d = c;
while(true) {
while (b <= c && x[b] <= v) {
if (x[b] == v)
swap(x, a++, b);
b++;
}
while (c >= b && x[c] >= v) {
if (x[c] == v)
swap(x, c, d--);
c--;
}
if (b > c)
break;
swap(x, b++, c--);
}
// Swap partition elements back to middle
int s, n = off + len;
s = Math.min(a-off, b-a ); vecswap(x, off, b-s, s);
s = Math.min(d-c, n-d-1); vecswap(x, b, n-s, s);
// Recursively sort non-partition-elements
if ((s = b-a) > 1)
sort1(x, off, s);
if ((s = d-c) > 1)
sort1(x, n-s, s);
}
四、针对double,float类型数组排序的sort()方法,采取了先把所有的数组元素值为-0.0d的转换成0.0d,再利用快速排序排好序,最后再还原。
public static long doubleToRawLongBits(double value)根据 IEEE 754 浮点“双精度格式”位布局,返回指定浮点值的表示形式,并保留 NaN 值。
第 63 位(掩码 0x8000000000000000L 选定的位)表示浮点数的符号。第 62-52 位(掩码 0x7ff0000000000000L 选定的位)表示指数。第 51-0 位(掩码 0x000fffffffffffffL 选定的位)表示浮点数的有效数字(有时也称为尾数)。
如果参数是正无穷大,则结果为 0x7ff0000000000000L。
如果参数是负无穷大,则结果为 0xfff0000000000000L。
如果参数是 NaN,则结果是表示实际 NaN 值的 long 整数。与 doubleToLongBits 方法不同,doubleToRawLongBits 并没有压缩那些将 NaN 编码为一个“规范的”NaN 值的所有位模式。
在所有情况下,结果都是一个 long 整数,将其赋予 longBitsToDouble(long) 方法将生成一个与 doubleToRawLongBits 的参数相同的浮点值。
参数:
value - 双精度 (double) 浮点数。
下面是源代码中的方法:
public static void sort(double[] a) {
sort2(a, 0, a.length);
}
private static void sort2(double a[], int fromIndex, int toIndex) {
//static long doubleToLongBits(double value)
//根据 IEEE 754 浮点双精度格式 ("double format") 位布局,返回指定浮点值的表示形式。
final long NEG_ZERO_BITS = Double.doubleToLongBits(-0.0d);
/*
* The sort is done in three phases to avoid the expense of using
* NaN and -0.0 aware comparisons during the main sort.
*/
/*
* Preprocessing phase: Move any NaN's to end of array, count the
* number of -0.0's, and turn them into 0.0's.
*/
int numNegZeros = 0;
int i = fromIndex, n = toIndex;
while(i < n) {
if (a[i] != a[i]) { //这段搞不懂,源代码怪怪的,感觉多此一举
double swap = a[i];
a[i] = a[--n];
a[n] = swap;
} else {
if (a[i]==0 && Double.doubleToLongBits(a[i])==NEG_ZERO_BITS) {
a[i] = 0.0d;
numNegZeros++;
}
i++;
}
}
// Main sort phase: quicksort everything but the NaN's
sort1(a, fromIndex, n-fromIndex);
// Postprocessing phase: change 0.0's to -0.0's as required
if (numNegZeros != 0) {
int j = binarySearch0(a, fromIndex, n, 0.0d); // posn of ANY zero
do {
j--;
} while (j>=0 && a[j]==0.0d);
// j is now one less than the index of the FIRST zero
for (int k=0; k<numNegZeros; k++)
a[++j] = -0.0d;
}
}