Arrays 类包含用于操作数组的各种方法,比如:排序、查找、填充、拷贝、相等判断等。

排序

Arrays.sort() 入参支持各种基本类型的数组,也支持自定义类型的数组,使用了双轴快速排序算法
下面 demo 为自定义类型的数组排序:

  1. @Test
  2. public void test16() {
  3. // ImmutableList 是 Guava 框架中的类
  4. ImmutableList<SortDTO> list = ImmutableList.of(
  5. new SortDTO(500),
  6. new SortDTO(100),
  7. new SortDTO(400),
  8. new SortDTO(200)
  9. );
  10. SortDTO[] array = new SortDTO[list.size()];
  11. list.toArray(array);
  12. Arrays.sort(array, new Comparator<SortDTO>() {
  13. @Override
  14. public int compare(SortDTO o1, SortDTO o2) {
  15. // 当方法的返回值大于 0 时,就将数组的前一个数和后一个数做交换。
  16. // 升序的话就o1 - o2
  17. // 降序的话就o2 - o1
  18. return o1.getSortTarget() - o2.getSortTarget();
  19. }
  20. });
  21. System.out.println(Arrays.toString(array));
  22. }
  1. public class SortDTO {
  2. private Integer sortTarget;
  3. public Integer getSortTarget() {
  4. return sortTarget;
  5. }
  6. public SortDTO(Integer sortTarget) {
  7. this.sortTarget = sortTarget;
  8. }
  9. @Override
  10. public String toString() {
  11. return "SortDTO{" +
  12. "sortTarget=" + sortTarget +
  13. '}';
  14. }
  15. }

查找

Arrays.binarySearch() 用于快速从数组中查找出对应的值。
返回参数对应数组下标的值,如果查询不到,则返回负数。
如果被搜索的数组是无序的,一定要先排序,否则二分搜索很有可能搜索不到
binarySearch() 的底层源码实现如下:

  1. private static <T> int binarySearch0(T[] a, int fromIndex, int toIndex,
  2. T key, Comparator<? super T> c) {
  3. // 比较器 c 为空,直接使用 key 的 Comparable.compareTo
  4. if (c == null) {
  5. // 使用内部排序器进行比较的方法
  6. return binarySearch0(a, fromIndex, toIndex, key);
  7. }
  8. int low = fromIndex;
  9. int high = toIndex - 1;
  10. // 开始位置小于结束位置,就会一直循环搜索
  11. while (low <= high) {
  12. int mid = (low + high) >>> 1;
  13. T midVal = a[mid];
  14. // 数组中间值和给定值大小比较
  15. int cmp = c.compare(midVal, key);
  16. // cmp < 0,代表中间值小于给定值
  17. if (cmp < 0) {
  18. low = mid + 1;
  19. } else if (cmp > 0) {
  20. high = mid - 1;
  21. } else {
  22. // 找到了
  23. return mid;
  24. }
  25. }
  26. // 返回值是负数,代表没有找到
  27. return -(low + 1);
  28. }

拷贝

数组拷贝,有时需要拷贝整个数组,有时需要拷贝部分,比如 ArrayList 在 add(扩容)或 remove(删除元素不是最后一个)操作时,会进行一些拷贝。
拷贝整个数组可以使用 copyOf(),拷贝部分可以使用 copyOfRange()。
copyOfRange() 的底层源码实现如下:

  1. // 注意:拷贝的数组区间是左闭右开的
  2. public static char[] copyOfRange(char[] original, int from, int to) {
  3. // 需要拷贝的长度
  4. int newLength = to - from;
  5. if (newLength < 0)
  6. throw new IllegalArgumentException(from + " > " + to);
  7. // 初始化新数组(拷贝的目标数组)
  8. char[] copy = new char[newLength];
  9. // 调用 native 方法进行拷贝
  10. System.arraycopy(original, from, copy, 0,
  11. Math.min(original.length - from, newLength));
  12. return copy;
  13. }

相等判断

Arrays.equals() 用于:比较一维数组,入参数组类型包括了八大基础数据和引用数据类型,入参为引用数据类型时,调用 object.equals() 进行元素的比较。
Arrays.deepEquals() 的入参只支持 Object[],用于:比较引用数据的多维数组的各元素是否相等,调用了 object.equals() 进行各元素的比较。


以 Object[] 为例说明 equals() 的底层代码实现

  1. public static boolean equals(Object[] a, Object[] a2) {
  2. if (a==a2)
  3. return true;
  4. if (a==null || a2==null)
  5. return false;
  6. int length = a.length;
  7. if (a2.length != length)
  8. return false;
  9. for (int i=0; i<length; i++) {
  10. Object o1 = a[i];
  11. Object o2 = a2[i];
  12. if (!(o1==null ? o2==null : o1.equals(o2)))
  13. return false;
  14. }
  15. return true;
  16. }

deepEquals() 的底层源码实现如下:

  1. public static boolean deepEquals(Object[] a1, Object[] a2) {
  2. if (a1 == a2)
  3. return true;
  4. if (a1 == null || a2==null)
  5. return false;
  6. int length = a1.length;
  7. if (a2.length != length)
  8. return false;
  9. for (int i = 0; i < length; i++) {
  10. Object e1 = a1[i];
  11. Object e2 = a2[i];
  12. if (e1 == e2)
  13. continue;
  14. if (e1 == null)
  15. return false;
  16. // Figure out whether the two elements are equal
  17. // 判断这两个元素是否相等
  18. boolean eq = deepEquals0(e1, e2);
  19. if (!eq)
  20. return false;
  21. }
  22. return true;
  23. }
  1. static boolean deepEquals0(Object e1, Object e2) {
  2. assert e1 != null;
  3. boolean eq;
  4. if (e1 instanceof Object[] && e2 instanceof Object[])
  5. eq = deepEquals ((Object[]) e1, (Object[]) e2);
  6. else if (e1 instanceof byte[] && e2 instanceof byte[])
  7. eq = equals((byte[]) e1, (byte[]) e2);
  8. else if (e1 instanceof short[] && e2 instanceof short[])
  9. eq = equals((short[]) e1, (short[]) e2);
  10. else if (e1 instanceof int[] && e2 instanceof int[])
  11. eq = equals((int[]) e1, (int[]) e2);
  12. else if (e1 instanceof long[] && e2 instanceof long[])
  13. eq = equals((long[]) e1, (long[]) e2);
  14. else if (e1 instanceof char[] && e2 instanceof char[])
  15. eq = equals((char[]) e1, (char[]) e2);
  16. else if (e1 instanceof float[] && e2 instanceof float[])
  17. eq = equals((float[]) e1, (float[]) e2);
  18. else if (e1 instanceof double[] && e2 instanceof double[])
  19. eq = equals((double[]) e1, (double[]) e2);
  20. else if (e1 instanceof boolean[] && e2 instanceof boolean[])
  21. eq = equals((boolean[]) e1, (boolean[]) e2);
  22. else
  23. eq = e1.equals(e2);
  24. return eq;
  25. }

数组->集合

把数组转化成集合时,常使用 Arrays.asList(array),这个方法有两个坑

  • 数组被修改后,会直接影响到集合
  • 不能对集合进行 add、remove 等操作,否则运行时会报 UnsupportedOperationExceptio

以下 demo 用于演示这两个坑:

  1. @Test
  2. public void test18() {
  3. Integer[] array = new Integer[]{1, 2, 3, 4, 5, 6};
  4. List<Integer> list = Arrays.asList(array);
  5. // 打印结果为:1
  6. System.out.println(list.get(0));
  7. array[0] = 10;
  8. // 打印结果为:10
  9. System.out.println(list.get(0));
  10. // 抛出 java.lang.UnsupportedOperationException
  11. list.add(7);
  12. }

Arrays.asList 方法返回的 List 并不是 java.util.ArrayList,而是自己内部的一个静态类,该静态类直接持有数组的引用,并且没有实现 add、remove 等方法,这就是坑的原因。
asList() 的底层源码实现如下:

  1. public static <T> List<T> asList(T... a) {
  2. // 此处 new 的 ArrayList,并不是 java.util.ArrayList
  3. // 而是自己内部的静态类
  4. return new ArrayList<>(a);
  5. }
  6. private static class ArrayList<E> extends AbstractList<E>
  7. implements RandomAccess, java.io.Serializable {
  8. private static final long serialVersionUID = -2764017481108945198L;
  9. // 这里的数组直接指向转化的数组
  10. private final E[] a;
  11. ArrayList(E[] array) {
  12. a = Objects.requireNonNull(array);
  13. }
  14. @Override
  15. public int size() {
  16. return a.length;
  17. }
  18. @Override
  19. public Object[] toArray() {
  20. return a.clone();
  21. }
  22. @Override
  23. @SuppressWarnings("unchecked")
  24. public <T> T[] toArray(T[] a) {
  25. int size = size();
  26. if (a.length < size)
  27. return Arrays.copyOf(this.a, size,
  28. (Class<? extends T[]>) a.getClass());
  29. System.arraycopy(this.a, 0, a, 0, size);
  30. if (a.length > size)
  31. a[size] = null;
  32. return a;
  33. }
  34. @Override
  35. public E get(int index) {
  36. return a[index];
  37. }
  38. @Override
  39. public E set(int index, E element) {
  40. E oldValue = a[index];
  41. a[index] = element;
  42. return oldValue;
  43. }
  44. @Override
  45. public int indexOf(Object o) {
  46. E[] a = this.a;
  47. if (o == null) {
  48. for (int i = 0; i < a.length; i++)
  49. if (a[i] == null)
  50. return i;
  51. } else {
  52. for (int i = 0; i < a.length; i++)
  53. if (o.equals(a[i]))
  54. return i;
  55. }
  56. return -1;
  57. }
  58. @Override
  59. public boolean contains(Object o) {
  60. return indexOf(o) != -1;
  61. }
  62. @Override
  63. public Spliterator<E> spliterator() {
  64. return Spliterators.spliterator(a, Spliterator.ORDERED);
  65. }
  66. @Override
  67. public void forEach(Consumer<? super E> action) {
  68. Objects.requireNonNull(action);
  69. for (E e : a) {
  70. action.accept(e);
  71. }
  72. }
  73. @Override
  74. public void replaceAll(UnaryOperator<E> operator) {
  75. Objects.requireNonNull(operator);
  76. E[] a = this.a;
  77. for (int i = 0; i < a.length; i++) {
  78. a[i] = operator.apply(a[i]);
  79. }
  80. }
  81. @Override
  82. public void sort(Comparator<? super E> c) {
  83. Arrays.sort(a, c);
  84. }
  85. }