java.util.Arrays
是数组工具,虽然源码有5000多行,但是考虑了多种数据类型,实现方法大致相同,理解起来很容易,主要有以下几类方法:
sort
:为各种类型的数组排序binarySearch
:二分查找equals
:判断相等fill
:将一个值填充到数组各项copyOf
:从头拷贝数组内容copyOfRange
:按范围拷贝数组内容hashCode
:计算数组的哈希值toString
:数组转字符串asList
:数组转ListparallelSort
:并行排序(1.8版本)parallelPrefix
:并行前缀(1.8版本)spliterator
:转成分离器(1.8版本)stream
:转成流形式(1.8版本)
1 sort
下面是对int[]
类型进行排序的方法,此外以下类型都使用了类似的方法:long[]
、short[]
、char[]
、byte[]
、float[]
和double[]
。
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); }
可以看到Arrays.sort
方法在进行数组范围检查后,直接调用了DualPivotQuicksort.sort
方法,其中Arrays.rangeCheck
方法的实现如下:
private static void rangeCheck(int arrayLength, int fromIndex, int toIndex) { if (fromIndex > toIndex) { throw new IllegalArgumentException( "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); } if (fromIndex < 0) { throw new ArrayIndexOutOfBoundsException(fromIndex); } if (toIndex > arrayLength) { throw new ArrayIndexOutOfBoundsException(toIndex); } }
2 binarySearch
下面是对int[]
类型进行二分查找的方法,此外以下类型都使用了类似的方法:long[]
、short[]
、char[]
、byte[]
、float[]
、double[]
和Object[]
。
public static int binarySearch(int[] a, int key) { return binarySearch0(a, 0, a.length, key); } public static int binarySearch(int[] a, int fromIndex, int toIndex, int key) { rangeCheck(a.length, fromIndex, toIndex); return binarySearch0(a, fromIndex, toIndex, 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. }
其中关于float和double类型的比较笔者将在equals方法中一同说明,下面再看一下对Object[]
进行二分查找的实现:
private static int binarySearch0(Object[] a, int fromIndex, int toIndex, Object key) { int low = fromIndex; int high = toIndex - 1; while (low <= high) { int mid = (low + high) >>> 1; @SuppressWarnings("rawtypes") Comparable midVal = (Comparable)a[mid]; @SuppressWarnings("unchecked") int cmp = midVal.compareTo(key); if (cmp < 0) low = mid + 1; else if (cmp > 0) high = mid - 1; else return mid; // key found } return -(low + 1); // key not found. }
可以看到将Object对象强制转换为Comparable对象,这种方法日后可以借鉴。
3 equals
下面是对int[]
类型进行相等比较的方法,此外以下类型都使用了类似的方法:long[]
、short[]
、char[]
、byte[]
、float[]
、double[]
、boolean[]
和Object[]
。
public static boolean equals(int[] a, int[] a2) { if (a==a2) return true; if (a==null || a2==null) return false; int length = a.length; if (a2.length != length) return false; for (int i=0; i<length; i++) if (a[i] != a2[i]) return false; return true; }
可以看到比较的逻辑十分清晰:
- 内存地址相等,返回
true
- 任意一者为
null
,返回false
- 如果数组长度不相等,返回
false
- 依次比较数组的元素,有不相等的情况直接返回
false
- 最后返回
true
各种数据类型的区别就在于依次比较数组元素的地方,其中long[]
、short[]
、char[]
、byte[]
、boolean[]
,都与int[]
的实现一样。
下面看一下float[]
和double[]
和Object[]
的相应实现:
for (int i=0; i<length; i++) if (Float.floatToIntBits(a[i])!=Float.floatToIntBits(a2[i])) return false;
for (int i=0; i<length; i++) if (Double.doubleToLongBits(a[i])!=Double.doubleToLongBits(a2[i])) return false;
for (int i=0; i<length; i++) { Object o1 = a[i]; Object o2 = a2[i]; if (!(o1==null ? o2==null : o1.equals(o2))) return false; }
这三种方法日后可以借鉴。
4 fill
下面是对int[]
类型进行数组填充的方法,此外以下类型都使用了相同的方法:long[]
、short[]
、char[]
、byte[]
、float[]
、double[]
、boolean[]
和Object[]
。
public static void fill(int[] a, int val) { for (int i = 0, len = a.length; i < len; i++) a[i] = val; } public static void fill(int[] a, int fromIndex, int toIndex, int val) { rangeCheck(a.length, fromIndex, toIndex); for (int i = fromIndex; i < toIndex; i++) a[i] = val; }
5 copyOf
下面是对int[]
类型进行从头拷贝数组的方法,此外以下类型都使用了相同的方法:long[]
、short[]
、char[]
、byte[]
、float[]
、double[]
和boolean[]
。
public static int[] copyOf(int[] original, int newLength) { int[] copy = new int[newLength]; System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; }
创建一个长度为newLength
的新数组,再调用本地方法System.arraycopy
将两个数组的内容进行拷贝,拷贝的长度为原数组长度和新数组长度的较小值。
下面是Object[]
的相应实现,主要的区别在创建新数组的过程:
@SuppressWarnings("unchecked") public static <T> T[] copyOf(T[] original, int newLength) { return (T[]) copyOf(original, newLength, original.getClass()); } public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { @SuppressWarnings("unchecked") 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; }
6 rangeCopy
下面是对int[]
类型进行按范围拷贝数组的方法,此外以下类型都使用了相同的方法:long[]
、short[]
、char[]
、byte[]
、float[]
、double[]
和boolean[]
。
public static int[] copyOfRange(int[] original, int from, int to) { int newLength = to - from; if (newLength < 0) throw new IllegalArgumentException(from + " > " + to); int[] copy = new int[newLength]; System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); return copy; }
下面是Object[]
的相应实现,主要的区别在创建新数组的过程:
@SuppressWarnings("unchecked") public static <T> T[] copyOfRange(T[] original, int from, int to) { return copyOfRange(original, from, to, (Class<? extends T[]>) original.getClass()); } public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) { int newLength = to - from; if (newLength < 0) throw new IllegalArgumentException(from + " > " + to); @SuppressWarnings("unchecked") T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); return copy; }
7 hashCode
下面是对int[]
类型进行计算哈希值的方法,此外以下类型都使用了相同的方法:short[]
、char[]
、byte[]
。
public static int hashCode(int a[]) { if (a == null) return 0; int result = 1; for (int element : a) result = 31 * result + element; return result; }
其中boolean[]
的实现有如下区别:
for (boolean element : a) result = 31 * result + (element ? 1231 : 1237);
其中long[]
的实现有如下区别:
for (long element : a) { int elementHash = (int)(element ^ (element >>> 32)); result = 31 * result + elementHash; }
其中float[]
的实现有如下区别:
for (float element : a) result = 31 * result + Float.floatToIntBits(element);
其中double[]
的实现有如下区别:
for (double element : a) { long bits = Double.doubleToLongBits(element); result = 31 * result + (int)(bits ^ (bits >>> 32)); }
其中Object[]
的实现有如下区别:
for (Object element : a) result = 31 * result + (element == null ? 0 : element.hashCode());
8 toString
下面是对int[]
类型进行转字符串的方法,此外以下类型都使用了类似的方法:long[]
、short[]
、char[]
、byte[]
、float[]
、double[]
、boolean[]
和Object[]
。
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(", "); } }
9 asList
@SafeVarargs @SuppressWarnings("varargs") public static <T> List<T> asList(T... a) { return new ArrayList<>(a); }
其中ArrayList是java.util.Arrays.ArrayList
。
10 小结
在实现集合框架的过程中,有调用Arrays的静态方法,因此笔者在这里对Arrays的相关方法进行了说明。