• JDK1.8源码(四)——java.util.Arrays类


    一、概述

    1、介绍

      Arrays 类是 JDK1.2 提供的一个工具类,提供处理数组的各种方法,基本上都是静态方法,能直接通过类名Arrays调用。

    二、类源码

    1、asList()方法

      将一个泛型数组转化为List集合返回。但是,这个List集合既不是ArrayList实例,也不是Vector实例。它是一个固定长度的 List 集合,是 Arrays 的一个内部类 java.util.Arrays.ArrayList。
      代码示例:使用

     1 public class Main {
     2     public static void main(String[] args) throws Exception {
     3         Integer[] d = {3, 1, 2};
     4         final List<Integer> integers = Arrays.asList(d);
     5         System.out.println(integers);
     6     }
     7 }
     8 
     9 // 结果
    10 [3, 1, 2]

      源码示例:

    1 public static <T> List<T> asList(T... a) {
    2     return new ArrayList<>(a);
    3 }
    4 
    5 private static class ArrayList<E> extends AbstractList<E>
    6     implements RandomAccess, java.io.Serializable
    7 {
    8     // 源码省略,读者可自行用idea查看
    9 }

      说明:
      ①、定长列表,只能对其进行查看或者修改不能进行添加或者删除操作

     1 public class Main {
     2     public static void main(String[] args) {
     3         String[] str = {"a", "b", "c"};
     4         List<String> list = Arrays.asList(str);
     5         // 可以进行修改
     6         list.set(1, "e");
     7         System.out.println(list.toString()); // [a, e, c]
     8 
     9         list.add("a"); //添加元素会报错 java.lang.UnsupportedOperationException
    10     }
    11 }

      查看源码发现,该类没有add() 和 remove()方法。如果对其进行增加或者删除操作,会调用其父类 AbstractList 对应的方法,而追溯父类的方法最终会抛出 UnsupportedOperationException 异常。源码如下:

    1 // 类 AbstractList
    2 public boolean add(E e) {
    3     add(size(), e);
    4     return true;
    5 }
    6 
    7 public void add(int index, E element) {
    8     throw new UnsupportedOperationException();
    9 }

      ②、引用类型的数组和基本类型的数组区别

     1 public class Main {
     2     public static void main(String[] args) {
     3         String[] str = {"a", "b", "c"};
     4         final List<String> list = Arrays.asList(str);
     5         System.out.println(list.size()); // 3
     6 
     7         int[] i = {1, 2, 3};
     8         final List<int[]> ints = Arrays.asList(i);
     9         System.out.println(ints.size()); // 1
    10         
    11         Integer[] in = {1, 2, 3};
    12         final List<Integer> integers = Arrays.asList(in);
    13         System.out.println(integers.size()); // 3
    14     }
    15 }

      类类型才是泛型,基本数据类型不能作为泛型的参数。读者根据上面结果自行体会一下。

      ③、返回的是原数组的里的引用,不是独立出来的集合对象

     1 public class Main {
     2     public static void main(String[] args) {
     3         String[] str = {"a", "b", "c"};
     4         List<String> listStr = Arrays.asList(str);
     5         System.out.println(Arrays.toString(str)); // [a, b, c]
     6 
     7         listStr.set(0, "d");
     8 
     9         System.out.println(Arrays.toString(str)); // [d, b, c]
    10     }
    11 }

      这里,修改的是返回的集合的内容,但是原数组的内容也变化了,所以只是返回了原数组的一个视图。如果希望返回一个全新的集合,可以如下:

     1 public class Main {
     2     public static void main(String[] args) {
     3         String[] str = {"a", "b", "c"};
     4 
     5         ArrayList<String> strings = new ArrayList<>(Arrays.asList(str));
     6         strings.add("d");
     7 
     8         System.out.println(Arrays.toString(str)); // [a, b, c]
     9         System.out.println(strings); // [a, b, c, d]
    10     }
    11 }

    2、sort()方法

      用于数组排序,有一系列重载方法。注意,如果是 Object 类型,需要实现Comparable接口或者传入一个比较器 Comparator ,使其具有可比性。可以参考这篇。Java比较器。
      源码示例:

    1 public static void sort(int[] a) {
    2     DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    3 }

      这个方法的源码很长,分别对数组的长度进行了各种算法的划分,包括快速排序,插入排序,冒泡排序都有使用。详细源码可以参考这篇博客。

    3、binarySearch()方法

      用于数组查找,是基于二分查找算法实现,有一系列重载方法,适用于各种基本数据类型以及对象数组。
      值得注意的是:调用此方法,要求待查找的数组有序。存在,返回元素下标;不存在,返回一个负数(不是 -1)。
      源码示例:

     1 public static int binarySearch(int[] a, int key) {
     2     return binarySearch0(a, 0, a.length, key);
     3 }
     4 
     5 // 典型的二分查找算法
     6 private static int binarySearch0(int[] a, int fromIndex, int toIndex,
     7                                  int key) {
     8     int low = fromIndex;
     9     int high = toIndex - 1;
    10 
    11     while (low <= high) {
    12         int mid = (low + high) >>> 1;
    13         int midVal = a[mid];
    14 
    15         if (midVal < key)
    16             low = mid + 1;
    17         else if (midVal > key)
    18             high = mid - 1;
    19         else
    20             return mid; // key found
    21     }
    22     
    23     // 找不到,返回并不是 -1
    24     return -(low + 1);  // key not found.
    25 }

    4、copyOf()方法

      拷贝数组元素。底层采用 System.arraycopy() 实现,这是一个native方法。
      注意:这个方法在ArrayList源码扩容时,也是用的它。
      代码示例:

     1 public class Main {
     2     public static void main(String[] args) {
     3         int[] old = {1, 3, 2};
     4 
     5         int[] ints = Arrays.copyOf(old, 5);
     6         System.out.println(Arrays.toString(ints)); // [1, 3, 2, 0, 0]
     7 
     8         int[] ints1 = Arrays.copyOf(old, 1);
     9         System.out.println(Arrays.toString(ints1)); // [1]
    10     }
    11 }

      源码示例:

     1 // Arrays类
     2 public static int[] copyOf(int[] original, int newLength) {
     3     int[] copy = new int[newLength];
     4     System.arraycopy(original, 0, copy, 0,
     5                     // 长度是旧数组长度 与 新长度 取小
     6                      Math.min(original.length, newLength));
     7     return copy;
     8 }
     9 
    10 // System类
    11 public static native void arraycopy(Object src,  int  srcPos,
    12                                     Object dest, int destPos,
    13                                     int length);

      src:源数组
      srcPos:源数组要复制的起始位置
      dest:目的数组
      destPos:目的数组放置的起始位置
      length:复制的长度
      注意:src 和 dest都必须是同类型或者可以进行转换类型的数组。

    5、equals()/deepEquals()方法

      ①、equals
      用于比较两个数组中对应位置的每一个元素是否相等。
      源码示例:

     1 // 基本数据类型数组比较
     2 public static boolean equals(int[] a, int[] a2) {
     3     // 引用相等,则相同
     4     if (a==a2)
     5         return true;
     6     if (a==null || a2==null)
     7         return false;
     8 
     9     int length = a.length;
    10     // 长度不同,则不相同
    11     if (a2.length != length)
    12         return false;
    13 
    14     // 循环依次比较数组中每个元素是否相等
    15     for (int i=0; i<length; i++)
    16         if (a[i] != a2[i])
    17             return false;
    18 
    19     return true;
    20 }
    21 
    22 // 引用类型数组比较
    23 public static boolean equals(Object[] a, Object[] a2) {
    24     if (a==a2)
    25         return true;
    26     if (a==null || a2==null)
    27         return false;
    28 
    29     int length = a.length;
    30     if (a2.length != length)
    31         return false;
    32 
    33     for (int i=0; i<length; i++) {
    34         Object o1 = a[i];
    35         Object o2 = a2[i];
    36         
    37         // 对象相同通过 equals 方法判断
    38         if (!(o1==null ? o2==null : o1.equals(o2)))
    39             return false;
    40     }
    41 
    42     return true;
    43 }

      ②、deepEquals
      比较两个数组的元素是否相等,可以嵌套任意层次的数组。
      源码就是递归的使用 deepEquals 判断每一层的数组是否相同。
      代码示例:

    1 public class Main {
    2     public static void main(String[] args) {
    3         String[][] name1 = {{"G", "a", "o"}, {"H", "u", "a", "n"}, {"j", "i", "e"}};
    4         String[][] name2 = {{"G", "a", "o"}, {"H", "u", "a", "n"}, {"j", "i", "e"}};
    5 
    6         System.out.println(Arrays.equals(name1, name2));// false
    7         System.out.println(Arrays.deepEquals(name1, name2));// true
    8     }
    9 }

    6、fill()方法

      该系列方法用于给数组赋值,并能指定某个范围赋值。
      代码示例:

    1 public class Main {
    2     public static void main(String[] args) {
    3         int[] arr = new int[4];
    4         System.out.println(Arrays.toString(arr)); // [0, 0, 0, 0]
    5 
    6         Arrays.fill(arr, 6);
    7         System.out.println(Arrays.toString(arr)); // [6, 6, 6, 6]
    8     }
    9 }

      源码示例:

     1 // 不写注释也能看懂的代码
     2 public static void fill(int[] a, int val) {
     3     for (int i = 0, len = a.length; i < len; i++)
     4         a[i] = val;
     5 }
     6 
     7 public static void fill(int[] a, int fromIndex, int toIndex, int val) {
     8     rangeCheck(a.length, fromIndex, toIndex);
     9     for (int i = fromIndex; i < toIndex; i++)
    10         a[i] = val;
    11 }

    7、toString 和 deepToString方法

      toString 用来打印一维数组的元素,而 deepToString 用来打印多层次嵌套的数组元素。

      参考文档:https://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html

    作者:Craftsman-L

    本博客所有文章仅用于学习、研究和交流目的,版权归作者所有,欢迎非商业性质转载。

    如果本篇博客给您带来帮助,请作者喝杯咖啡吧!点击下面打赏,您的支持是我最大的动力!

  • 相关阅读:
    如何提高IT团队的执行力?
    Internet Explorer 安全区域注册表项说明
    用C#编写ActiveX控件(一) 转载
    用C#编写ActiveX控件(二) 转载
    关于《用C#编写ActiveX控件》的几点说明 转载
    【经典】单调栈+离线+线段树区间更新——求所有子区间gcd之和 icpc cerc 2019 b
    SQL语句
    Windows 程序设计学习记录(1)类的定义和应用
    Windows 程序设计学习记录(2)数据链表删除,增加的算法
    推荐一些博客网站
  • 原文地址:https://www.cnblogs.com/originator/p/15335862.html
Copyright © 2020-2023  润新知