zoukankan      html  css  js  c++  java
  • JDK1.8源码——java.util.Arrays 类


     java.util.Arrays 类是 JDK 提供的一个工具类,用来处理数组的各种方法,而且每个方法基本上都是静态方法,能直接通过类名Arrays调用。

    1、asList

        public static <T> List<T> asList(T... a) {
            return new ArrayList<>(a);
        }

      作用是返回由指定数组支持的固定大小列表

      注意:这个方法返回的 ArrayList 不是我们常用的集合类 java.util.ArrayList。这里的 ArrayList 是 Arrays 的一个内部类 java.util.Arrays.ArrayList。这个内部类有如下属性和方法:

      

     1 private static class ArrayList<E> extends AbstractList<E>
     2         implements RandomAccess, java.io.Serializable
     3     {
     4         private static final long serialVersionUID = -2764017481108945198L;
     5         private final E[] a;
     6 
     7         ArrayList(E[] array) {
     8             if (array==null)
     9                 throw new NullPointerException();
    10             a = array;
    11         }
    12 
    13         public int size() {
    14             return a.length;
    15         }
    16 
    17         public Object[] toArray() {
    18             return a.clone();
    19         }
    20 
    21         public <T> T[] toArray(T[] a) {
    22             int size = size();
    23             if (a.length < size)
    24                 return Arrays.copyOf(this.a, size,
    25                                      (Class<? extends T[]>) a.getClass());
    26             System.arraycopy(this.a, 0, a, 0, size);
    27             if (a.length > size)
    28                 a[size] = null;
    29             return a;
    30         }
    31 
    32         public E get(int index) {
    33             return a[index];
    34         }
    35 
    36         public E set(int index, E element) {
    37             E oldValue = a[index];
    38             a[index] = element;
    39             return oldValue;
    40         }
    41 
    42         public int indexOf(Object o) {
    43             if (o==null) {
    44                 for (int i=0; i<a.length; i++)
    45                     if (a[i]==null)
    46                         return i;
    47             } else {
    48                 for (int i=0; i<a.length; i++)
    49                     if (o.equals(a[i]))
    50                         return i;
    51             }
    52             return -1;
    53         }
    54 
    55         public boolean contains(Object o) {
    56             return indexOf(o) != -1;
    57         }
    58     }

      ①、返回的 ArrayList 数组是一个定长列表,我们只能对其进行查看或者修改,但是不能进行添加或者删除操作

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

    1 String[] str = {"a","b","c"};
    2 List<String> listStr = Arrays.asList(str);
    3 listStr.set(1, "e");//可以进行修改
    4 System.out.println(listStr.toString());//[a, e, c]
    5 listStr.add("a");//添加元素会报错 java.lang.UnsupportedOperationException 

      

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

    复制代码
    1 String[] str = {"a","b","c"};
    2 List listStr = Arrays.asList(str);
    3 System.out.println(listStr.size());//3
    4 
    5 int[] i = {1,2,3};
    6 List listI = Arrays.asList(i);
    7 System.out.println(listI.size());//1
    复制代码

      上面的结果第一个listStr.size()==3,而第二个 listI.size()==1。这是为什么呢?

      我们看源码,在 Arrays.asList 中,方法声明为  <T> List<T> asList(T... a)。该方法接收一个可变参数,并且这个可变参数类型是作为泛型的参数。我们知道基本数据类型是不能作为泛型的参数的,但是数组是引用类型,所以数组是可以泛型化的,于是 int[] 作为了整个参数类型,而不是 int 作为参数类型。

      所以将上面的方法泛型化补全应该是:

    复制代码
     1 String[] str = {"a","b","c"};
     2 List<String> listStr = Arrays.asList(str);
     3 System.out.println(listStr.size());//3
     4 
     5 int[] i = {1,2,3};
     6 List<int[]> listI = Arrays.asList(i);//注意这里List参数为 int[] ,而不是 int
     7 System.out.println(listI.size());//1
     8 
     9 Integer[] in = {1,2,3};
    10 List<Integer> listIn = Arrays.asList(in);//这里参数为int的包装类Integer,所以集合长度为3
    11 System.out.println(listIn.size());//3
    复制代码

      ③、返回的列表ArrayList里面的元素都是引用,不是独立出来的对象

    复制代码
    1 String[] str = {"a","b","c"};
    2 List<String> listStr = Arrays.asList(str);
    3 //执行更新操作前
    4 System.out.println(Arrays.toString(str));//[a, b, c]
    5 listStr.set(0, "d");//将第一个元素a改为d
    6 //执行更新操作后
    7 System.out.println(Arrays.toString(str));//[d, b, c]
    复制代码

      这里的Arrays.toString()方法就是打印数组的内容,后面会介绍。我们看修改集合的内容,原数组的内容也变化了,所以这里传入的是引用类型。

      ④、已知数组数据,如何快速获取一个可进行增删改查的列表List?

    1 String[] str = {"a","b","c"};
    2 List<String> listStr = new ArrayList<>(Arrays.asList(str));
    3 listStr.add("d");
    4 System.out.println(listStr.size());//4

      这里的ArrayList 集合类后面我们会详细讲解,大家目前只需要知道有这种用法即可。

      ⑤、Arrays.asList() 方法使用场景

      Arrays工具类提供了一个方法asList, 使用该方法可以将一个变长参数或者数组转换成List 。但是,生成的List的长度是固定的;能够进行修改操作(比如,修改某个位置的元素);不能执行影响长度的操作(如add、remove等操作),否则会抛出UnsupportedOperationException异常。

      所以 Arrays.asList 比较适合那些已经有数组数据或者一些元素,而需要快速构建一个List,只用于读取操作,而不进行添加或删除操作的场景。

    2、sort

      该方法是用于数组排序,在 Arrays 类中有该方法的一系列重载方法,能对7种基本数据类型,包括 byte,char,double,float,int,long,short 等都能进行排序,还有 Object 类型(实现了Comparable接口),以及比较器 Comparator 。

      

      ①、基本类型的数组

      这里我们以 int[ ] 为例看看:

    1 int[] num = {1,3,8,5,2,4,6,7};
    2 Arrays.sort(num);
    3 System.out.println(Arrays.toString(num));//[1, 2, 3, 4, 5, 6, 7, 8]

      通过调用 sort(int[] a) 方法,将原数组按照升序的顺序排列。下面我们通过源码看看是如何实现排序的:

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

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

      ②、对象类型数组

      该类型的数组进行排序可以实现 Comparable 接口,重写 compareTo 方法进行排序。

    1 String[] str = {"a","f","c","d"};
    2 Arrays.sort(str);
    3 System.out.println(Arrays.toString(str));//[a, c, d, f]

      String 类型实现了 Comparable 接口,内部的 compareTo 方法是按照字典码进行比较的。

      ③、没有实现Comparable接口的,可以通过Comparator实现排序

    复制代码
     1 Person[] p = new Person[]{new Person("zhangsan",22),new Person("wangwu",11),new Person("lisi",33)};
     2 Arrays.sort(p,new Comparator<Person>() {
     3     @Override
     4     public int compare(Person o1, Person o2) {
     5         if(o1 == null || o2 == null){
     6             return 0;
     7         }
     8         return o1.getPage()-o2.getPage();
     9     }
    10 });    
    11 System.out.println(Arrays.toString(p));
    复制代码

    3、binarySearch

      用二分法查找数组中的某个元素。该方法和 sort 方法一样,适用于各种基本数据类型以及对象。

      注意:二分法是对以及有序的数组进行查找(比如先用Arrays.sort()进行排序,然后调用此方法进行查找)。找到元素返回下标,没有则返回 -1

      实例:

    1 int[] num = {1,3,8,5,2,4,6,7};
    2 Arrays.sort(num);
    3 System.out.println(Arrays.toString(num));//[1, 2, 3, 4, 5, 6, 7, 8]
    4 System.out.println(Arrays.binarySearch(num, 2));//返回元素的下标 1

      具体源码实现:

     1 public static int binarySearch(int[] a, int key) {
     2         return binarySearch0(a, 0, a.length, key);
     3     }
     4     private static int binarySearch0(int[] a, int fromIndex, int toIndex,int key) {
     5         int low = fromIndex;
     6         int high = toIndex - 1;
     7         
     8         while (low <= high) {
     9             int mid = (low + high) >>> 1;//取中间值下标
    10             int midVal = a[mid];//取中间值
    11             
    12             if (midVal < key)
    13             low = mid + 1;
    14             else if (midVal > key)
    15             high = mid - 1;
    16             else
    17             return mid; 
    18         }
    19         return -(low + 1); 
    20     }

    4、copyOf

      拷贝数组元素。底层采用 System.arraycopy() 实现,这是一个native方法。

        public static native void arraycopy(Object src,  int  srcPos,
                                            Object dest, int destPos,
                                            int length);

      src:源数组

      srcPos:源数组要复制的起始位置

      dest:目的数组

      destPos:目的数组放置的起始位置

      length:复制的长度

      注意:src 和 dest都必须是同类型或者可以进行转换类型的数组。

    int[] num1 = {1,2,3};
    int[] num2 = new int[3];
    System.arraycopy(num1, 0, num2, 0, num1.length);
    System.out.println(Arrays.toString(num2));//[1, 2, 3]
    复制代码
        /**
         * @param original 源数组
         * @param newLength //返回新数组的长度
         * @return
         */
        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;
        }
    复制代码

    5、equals 和 deepEquals

      ①、equals

      equals 用来比较两个数组中对应位置的每个元素是否相等。

      

      八种基本数据类型以及对象都能进行比较。

      我们先看看 int类型的数组比较源码实现:

     1 public static boolean equals(int[] a, int[] a2) {
     2         if (a==a2)//数组引用相等,则里面的元素一定相等
     3             return true;
     4         if (a==null || a2==null)//两个数组其中一个为null,都返回false
     5             return false;
     6 
     7         int length = a.length;
     8         if (a2.length != length)//两个数组长度不等,返回false
     9             return false;
    10 
    11         for (int i=0; i<length; i++)//通过for循环依次比较数组中每个元素是否相等
    12             if (a[i] != a2[i])
    13                 return false;
    14 
    15         return true;
    16     }

      在看对象数组的比较:

     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 
     7         int length = a.length;
     8         if (a2.length != length)
     9             return false;
    10 
    11         for (int i=0; i<length; i++) {
    12             Object o1 = a[i];
    13             Object o2 = a2[i];
    14             if (!(o1==null ? o2==null : o1.equals(o2)))
    15                 return false;
    16         }
    17 
    18         return true;
    19     }

      基本上也是通过 equals 来判断。

      ②、deepEquals

      也是用来比较两个数组的元素是否相等,不过 deepEquals 能够进行比较多维数组,而且是任意层次的嵌套数组。

             String[][] name1 = {{ "G","a","o" },{ "H","u","a","n"},{ "j","i","e"}};  
             String[][] name2 = {{ "G","a","o" },{ "H","u","a","n"},{ "j","i","e"}};
             System.out.println(Arrays.equals(name1,name2));// false  
             System.out.println(Arrays.deepEquals(name1,name2));// true

    6、fill

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

    复制代码
        //给a数组所有元素赋值 val
        public static void fill(int[] a, int val) {
            for (int i = 0, len = a.length; i < len; i++)
                a[i] = val;
        }
        
        //给从 fromIndex 开始的下标,toIndex-1结尾的下标都赋值 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;
        }
    复制代码

    7、toString 和 deepToString

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

     1 public static String toString(int[] a) {
     2         if (a == null)
     3             return "null";
     4         int iMax = a.length - 1;
     5         if (iMax == -1)
     6             return "[]";
     7 
     8         StringBuilder b = new StringBuilder();
     9         b.append('[');
    10         for (int i = 0; ; i++) {
    11             b.append(a[i]);
    12             if (i == iMax)
    13                 return b.append(']').toString();
    14             b.append(", ");
    15         }
    16     }
    复制代码
     1     public static String toString(int[] a) {
     2         if (a == null)
     3             return "null";
     4         int iMax = a.length - 1;
     5         if (iMax == -1)
     6             return "[]";
     7 
     8         StringBuilder b = new StringBuilder();
     9         b.append('[');
    10         for (int i = 0; ; i++) {
    11             b.append(a[i]);
    12             if (i == iMax)
    13                 return b.append(']').toString();
    14             b.append(", ");
    15         }
    16     }
    复制代码
  • 相关阅读:
    页面可视化搭建 整理
    单页面应用(SPA)重新部署后,正在浏览的页面如何更新缓存?
    vim 使用
    浏览器缓存 知识点
    http 2.0 新特性
    GoJS 在 vue 项目中的使用
    详解Vue中watch的高级用法
    什么是 PWA?
    代码风格统一工具:EditorConfig 和 静态代码检查工具:ESLint
    vue-cli 3.x 使用
  • 原文地址:https://www.cnblogs.com/aaaazzzz/p/12824699.html
Copyright © 2011-2022 走看看