java.util.Arrays 是数组工具,虽然源码有5000多行,但是考虑了多种数据类型,实现方法大致相同,理解起来很容易,主要有以下几类方法:

  • sort:为各种类型的数组排序
  • binarySearch:二分查找
  • equals:判断相等
  • fill:将一个值填充到数组各项
  • copyOf:从头拷贝数组内容
  • copyOfRange:按范围拷贝数组内容
  • hashCode:计算数组的哈希值
  • toString:数组转字符串
  • asList:数组转List
  • parallelSort:并行排序(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的相关方法进行了说明。