Arrays
此类包含数组操作的各种方法(比如排序和搜索)。此类还包含一个允许将数组作为列表来查看的静态工厂。除非特别注明,否则如果指定数组引用为null,则此类中的方法都会抛出 NullPointerException
Arrays的主要方法:sort(),binarySearch(),equals(),copyOf(),toString()等。
我们先看看排序方法sort(),这里的排序会根据不同的排序对象而采取不同的排序方式,主要是快速排序(Dual-Pivot Quicksort)。到了JDK1.7专门把排序功能封装成一个类:DualPivotQuicksort
下面我们看看byte数组的排序sort(byte[]),这个比较简单,使用了计数排序(CountSort)和插入排序(InsertSort)。
【int[]的排序使用了快速排序(Quicksort)和归并排序(mergeSort),临界为286,大的用后者】
(具体不同排序的实现,待续,此处不做讨论。)
public static void sort(byte[] a) { DualPivotQuicksort.sort(a); } public static void sort(byte[] a, int left, int right) { // Use counting sort on large arrays if (right - left > COUNTING_SORT_THRESHOLD_FOR_BYTE) { int[] count = newint[NUM_BYTE_VALUES]; for (int i = left - 1; ++i <= right; count[a[i] - Byte.MIN_VALUE]++ ); for (int i = NUM_BYTE_VALUES, k = right + 1; k> left; ) { while (count[--i] == 0); byte value = (byte) (i + Byte.MIN_VALUE); int s = count[i]; do { a[--k] = value; } while (--s > 0); } } else { // Use insertion sort on small arrays for (int i = left, j = i; i < right; j = ++i) { byte ai = a[i + 1]; while (ai < a[j]) { a[j + 1] = a[j]; if (j-- == left) break; } a[j + 1] = ai; } } }
这里有几个地方是比较有意思的,比如说第一个计数排序中,使用k来处理相同元素的连续存放。
还有,简化了一个循环统计的写法,让我们看得很别扭,如下:
for (int i = left - 1; ++i <= right; count[a[i] - Byte.MIN_VALUE]++ );
替换成下面形式,更容易理解:
for (int i = 0; i < arrObj.length; i++) count[arrObj[i] - Byte.MIN_VALUE]++;
由上可知,由于byte中k的范围是256,所以时间复杂度为O(n+256)。因此当n少的时候用插入排序更快,临界为29。
/** If the length of a byte array to be sorted is greaterthan this constant, counting sort isused in preference to insertion sort. */ private static final int COUNTING_SORT_THRESHOLD_FOR_BYTE = 29;
二分法查找通过binarySearch()实现,但是前提是,原数组要是有序的,否则二分查找就没有意义了。
public static int binarySearch(int[] a, int key) { return binarySearch0(a, 0, a.length, key); } private static int binarySearch0(long[] a,int fromIndex,int toIndex,long key){ int low = fromIndex; int high = toIndex - 1; while (low <= high) { int mid = (low +high) >>> 1; long midVal = a[mid]; if (midVal < key) low = mid + 1; elseif (midVal > key) high = mid - 1; else return mid; // keyfound } return -(low + 1); // key not found. }
判断数组相等的标准是:次序和元素的完全相等。和String相等是一个标准,就是遍历下标,相同下标的元素都相等,数组才相等。
public static boolean equals(int[] a, int[] a2) { if (a==a2) returntrue; if (a==null || a2==null) returnfalse; int length = a.length; if (a2.length != length) returnfalse; for (int i=0; i<length; i++) if (a[i] != a2[i]) returnfalse; returntrue; }
复制数组copyOf(T[], int),从T[]复制int长度的数组元素,返回类型为T[]。这里使用了泛型,而复制工作由System.arraycopy()来完成。
这里的逻辑:先创建一个指定长度newLength的数组,填充null (数组初始化自动完成),然后从original复制指定长度Math.min(original.length, newLength),从0开始。
publicstatic <T> T[] copyOf(T[]original, int newLength) { return (T[]) copyOf(original,newLength, original.getClass()); } publicstatic <T,U> T[] copyOf(U[]original, int newLength, Class<? extends T[]> newType) { T[] copy = ((Object)newType ==(Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, 0,copy, 0, Math.min(original.length, newLength)); return copy; }
对于数组本身来说,toString只是继承Object.toString(),所以只会返回一个地址串,这样不便于观察数组。
所以我们可以通过Arrays.toString()来打印数组的详情:遍历每个数组元素,逐个toString()拼装成 [2,5,3,6]的形式。
另外,对于二维数组,可以使用deepToString()来进行深层次的toString。
public static String toString(int[] a) { if (a == null) return"null"; int iMax = a.length - 1; if (iMax == -1) return"[]"; StringBuilder b = new StringBuilder(); b.append('['); for (int i = 0; ; i++) { b.append(a[i]); if (i == iMax) return b.append(']').toString(); b.append(", "); } }
填充数组,from到to填充成val
publicstaticvoid fill(int[] a, int fromIndex, int toIndex, int val) { rangeCheck(a.length, fromIndex,toIndex); // 检查数组范围 for (int i = fromIndex; i < toIndex; i++) a[i] = val; }
hashCode的Time33算法,具体见HashMap.hashCode()或《Effective Java》,String中也是同样的实现,不过因为可变性,这里没有像String那样缓存起来。
public static int hashCode(int a[]) { if (a == null) return 0; int result = 1; for (int element : a) result = 31 * result + element; return result; }
asList方法是用了可变参数和泛型,只要输入的T,则返回ArrayList<T>的列表。
此处还使用注解【SafeVarargs】,说明可变长参数的方法在与泛型类一起使用时不会出现类型安全问题。
@SafeVarargs publicstatic <T> List<T> asList(T... a) { returnnewArrayList<>(a); }
这里的可变参数,让使用变得很灵活,下面两种形式都是可以的。
Integer[] arrSrc = {3,2,1}; List<Integer> list1 = Arrays.asList(arrSrc); List<Integer> list2 = Arrays.asList(3,2,1);
--源码取自JDK1.7