0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

JDK中java.lang.Arrays 类的源码解析

科技绿洲 来源:Java技术指北 作者:Java技术指北 2023-10-11 15:31 次阅读

日常开发中,我们会使用各种工具类,利用封装好的轮子,能让我们的开发事半功倍。但是在JDK中,有一个特别的工具类——java.lang.Arrays.class,其源码实现还是挺精湛,接下来让我们来揭开它神秘的面纱。

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。这个内部类有如下属性和方法:
图片

private static class ArrayList< E > extends AbstractList< E >
        implements RandomAccess, java.io.Serializable{
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;

        ArrayList(E[] array) {
            if (array==null){
                throw new NullPointerException();
            }
            a = array;
        }

        public int size() {
            return a.length;
        }

        public Object[] toArray() {
            return a.clone();
        }

        public < T > T[] toArray(T[] a) {
            int size = size();
            if (a.length < size){
                return Arrays.copyOf(this.a, size,(Class< ? extends T[] >) a.getClass());
            }
            System.arraycopy(this.a, 0, a, 0, size);
            if (a.length > size){
                a[size] = null;
            }   
            return a;
        }

        public E get(int index) {
            return a[index];
        }

        public E set(int index, E element) {
            E oldValue = a[index];
            a[index] = element;
            return oldValue;
        }

        public int indexOf(Object o) {
            if (o==null) {
                for (int i=0; i< a.length; i++){
                    if (a[i]==null){
                        return i;
                    }
                }
            } else {
                for (int i=0; i< a.length; i++){
                    if (o.equals(a[i])){
                        return i;
                    }
                }
            }
            return -1;
        }

        public boolean contains(Object o) {
            return indexOf(o) != -1;
        }
}

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

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

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

图片

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

String[] str = {"a","b","c"};
List listStr = Arrays.asList(str);
System.out.println(listStr.size());//3

int[] i = {1,2,3};
List listI = Arrays.asList(i);
System.out.println(listI.size());//1

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

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

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

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

int[] i = {1,2,3};
List< int[] > listI = Arrays.asList(i);//注意这里List参数为 int[] ,而不是 int
System.out.println(listI.size());//1

Integer[] in = {1,2,3};
List< Integer > listIn = Arrays.asList(in);//这里参数为int的包装类Integer,所以集合长度为3
System.out.println(listIn.size());//3

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

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

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

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

String[] str = {"a","b","c"};
 List< String > listStr = new ArrayList<  >(Arrays.asList(str));
 listStr.add("d");
 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[ ] 为例看看:

int[] num = {1,3,8,5,2,4,6,7};
 Arrays.sort(num);
 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 方法进行排序。

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

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

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

Person[] p = new Person[]{new Person("zhangsan",22),new Person("wangwu",11),new Person("lisi",33)};
Arrays.sort(p,new Comparator< Person >() {
    @Override
    public int compare(Person o1, Person o2) {
        if(o1 == null || o2 == null){
            return 0;
        }
        return o1.getPage()-o2.getPage();
    }
});    
System.out.println(Arrays.toString(p));

3、binarySearch

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

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

实例:

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

具体源码实现:

public static int binarySearch(int[] a, int key) {
        return binarySearch0(a, 0, a.length, 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; 
            }
        }
        return -(low + 1); 
}

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、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;
        }
    }

6、toString 和 deepToString

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

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(", ");
        }
    }

7、小结

好了,这就是JDK中java.lang.Arrays 类的源码解析。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    19

    文章

    2957

    浏览量

    104537
  • 源码
    +关注

    关注

    8

    文章

    633

    浏览量

    29135
  • 数组
    +关注

    关注

    1

    文章

    414

    浏览量

    25906
  • JDK
    JDK
    +关注

    关注

    0

    文章

    81

    浏览量

    16576
收藏 人收藏

    评论

    相关推荐

    JDK的安装、环境配置及使用

    ,变量值输入库名C:\jdk1.5\lib\dt.jar;C:\jdk1.5\lib\tools.jar;然后选择确定。再次选择“系统变量”的“新建”,在变量名
    发表于 12-06 00:19

    java jdk安装参考步骤

    1、把jdk-8u5-linux-x64.gz解压,然后把解压的文件夹放到/usr/lib/jvm/下,并重命名为jdk,这个目录可以自定义。2、编辑~/.basrc文件,在文件的末尾追加下面的命令
    发表于 09-25 16:43

    JAVAJDK、J2EE、J2SE、J2ME的概念详解

    开发工具包,基本上每个学java的人都会先在机器上装一个JDK,那他都包含哪几部分呢?让我们看一下JDK的安装目录。在目录下面有六个文件夹、一个src
    发表于 07-10 08:28

    加载机制的过程和策略

    java.lang,在使用引导加载器加载时会先加载JDK的String,因为这个本来是属
    发表于 01-05 17:21

    树莓派如何安装Java JDK

    。Oracle Java 具有其他一些商业功能,并且许可仅允许非商业用途。下面介绍如何在树莓派的 Raspbian OS 上安装Java(OpenJDK)。  运行以下命令安装最新的 JDK 版本,目前
    发表于 02-02 16:37

    java jdk6.0官方下载

    java jdk6.0下载如何件: java jdk6.0安装步骤: 第一步 JDK1.6的安装步骤 第一步双击安装文件
    发表于 10-17 11:47 155次下载
    <b class='flag-5'>java</b> <b class='flag-5'>jdk</b>6.0官方下载

    深入理解java枚举类型enum用法

    enum 的全称为 enumeration, 是 JDK 1.5 引入的新特性,存放在 java.lang。 下面是我在使用 enum 过程
    发表于 09-27 11:49 0次下载

    理解Java字符流与字节流的区别

    JDK称为Java开发包或Java开发工具,是一个编写Java的Applet小程序和应用程序的程序开发环境。JDK是整个
    发表于 12-04 10:39 1106次阅读
    理解<b class='flag-5'>Java</b><b class='flag-5'>中</b>字符流与字节流的区别

    基于java的负载均衡算法解析源码分享

    负载均衡的算法实际上就是解决跨系统调用的时候,在考虑后端机器承载情况的前提下,保证请求分配的平衡和合理。下面是基于java的负载均衡算法解析源码,以供参考。
    发表于 01-01 19:29 2206次阅读

    Java数组的常用方法_Java:数组工具Arrays的常用方法的用法及代码

    本文主要详细介绍了Java数组的常用方法以及数组工具Arrays的常用方法的用法及代码。
    发表于 01-29 10:25 2909次阅读

    基于JDK 1.8来分析Thread源码

    由上图我们可以看出,Thread实现了Runnable接口,而Runnable在JDK 1.8被@FunctionalInterface注解标记为函数式接口,Runnable接口在JDK
    的头像 发表于 02-06 17:12 593次阅读

    JavaArrays是什么 Arrays常用方法

    了解Arrays的概念 **A****rrays** 位于java.util包下,Arrays是一个操作数组的工具
    的头像 发表于 02-17 15:11 1019次阅读
    <b class='flag-5'>Java</b><b class='flag-5'>中</b><b class='flag-5'>Arrays</b><b class='flag-5'>类</b>是什么 <b class='flag-5'>Arrays</b>常用方法

    怎样运用Java实现冒泡排序和Arrays排序出来

    在数据结构我们学习了解了冒泡排序和Arrays排序的基本算法,但没能够用编程语言实现出来。那我们应该怎样运用Java通过编程语言将冒泡排序和Arrays排序实现出来呢?
    的头像 发表于 03-02 09:37 487次阅读
    怎样运用<b class='flag-5'>Java</b>实现冒泡排序和<b class='flag-5'>Arrays</b>排序出来

    JDKjava.lang.String 源码解析

    1、String 的定义 public final class String implements java.io.Serializable, Comparable, CharSequence
    的头像 发表于 10-13 10:51 452次阅读
    <b class='flag-5'>JDK</b><b class='flag-5'>中</b><b class='flag-5'>java.lang</b>.String <b class='flag-5'>类</b>的<b class='flag-5'>源码</b><b class='flag-5'>解析</b>

    weblogic修改jdk路径

    WebLogic是一个流行的Java应用服务器,可以用于部署和管理企业级Java应用程序。在WebLogic的安装和配置过程,我们可能会遇到需要修改JDK
    的头像 发表于 12-05 14:46 1245次阅读