深入理解List的toArray()方法和toArray(T[] a)方法「建议收藏」

深入理解List的toArray()方法和toArray(T[] a)方法「建议收藏」深入理解List的toArray()方法和toArray(T[]a)方法这两个方法都是将列表List中的元素转导出为数组,不同的是,toArray()方法导出的是Object类型数组,而toArray[T[]a]方法导出的是指定类型的数组。下面是两个方法的申明及说明,摘自Java8的API文档。toArray()方法的分析Object[]toArray()Returnsan…

大家好,又见面了,我是你们的朋友全栈君。

深入理解List的toArray()方法和toArray(T[] a)方法

这两个方法都是将列表List中的元素转导出为数组,不同的是,toArray()方法导出的是Object类型数组,而toArray[T[] a]方法导出的是指定类型的数组。

下面是两个方法的申明及说明,摘自Java8的API文档。

toArray()方法的分析

Object[] toArray()

Returns an array containing all of the elements in this list in proper sequence (from first to last element).

The returned array will be “safe” in that no references to it are maintained by this list. (In other words, this method must allocate a new array even if this list is backed by an array). The caller is thus free to modify the returned array.

This method acts as bridge between array-based and collection-based APIs.

  • Specified by:

toArray in interface Collection<E>

  • Returns:

an array containing all of the elements in this list in proper sequence

  • See Also:

Arrays.asList(Object[\])

toArray()方法会返回List中所有元素构成的数组,并且数组类型是Object[]。还要注意一点就是,返回的数组是新生成的一个数组,也就是说,多次运行toArray()方法会获得不同的数组对象,但是这些数组对象中内容一样的。也就是说,toArray()返回的数组是安全的,你可以对它进行任意的修改,其原因就是List不会维持一个对该返回的数组的引用。下面我们做一个小实验来验证一下:

public static void main(String[] args) { 
   
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    Object[] objects1 = list.toArray();
    Object[] objects2 = list.toArray();
    System.out.println("objects1 == objects2 : "+(objects1 == objects2));
    objects1[1]=4;
    System.out.println("show objects1: "+ Arrays.toString(objects1));
    System.out.println("show objects2: "+ Arrays.toString(objects2));
    System.out.println("show list: "+list);
}

输出结果为:

objects1 == objects2 : false
show objects1: [1, 4]
show objects2: [1, 2]
show list: [1, 2]

从这个小实验里面,可以看出确实toArray()返回的是一个新的数组对象,并且多次执行toArray()方法获得的是不同的数组对象,并且对其中一个数组进行修改,不会影响到其他toArray()方法获得的数组,并且也不会影响到list本身原来存储的元素值。

这儿存在一个问题,list中存储的是基本类型int的包装类型Integer,如果换成其他的自定义类型呢,结果会是怎么样?接下来我们看下面这个例子。

private static class People{ 
   
    String name;
    public People(String name){ 
   
        this.name = name;
    }

    @Override
    public String toString() { 
   
        return "People{" +
                "name='" + name + '\'' +
                '}';
    }
}

public static void main(String[] args) { 
   
    List<People> list = new ArrayList<>();
    list.add(new People("小明"));
    list.add(new People("小王"));
    Object[] objects1 = list.toArray();
    Object[] objects2 = list.toArray();
    System.out.println("objects1 == objects2 : "+(objects1 == objects2));
    objects1[1]=new People("小花");
    System.out.println("show objects1: "+ Arrays.toString(objects1));
    System.out.println("show objects2: "+ Arrays.toString(objects2));
    System.out.println("show list: "+list);
}

运行结果:

objects1 == objects2 : false
show objects1: [People{name=‘小明’}, People{name=‘小花’}]
show objects2: [People{name=‘小明’}, People{name=‘小王’}]
show list: [People{name=‘小明’}, People{name=‘小王’}]

可以看到依然是和上面的分析结果一样,toArray()返回的是一个新的数组对象,对于toArray()返回的一个数组元素进行修改,不会影响到其他toArray()返回的数组对象,也不会影响list本身。

现在元素是自定义对象,那么我们对元素对象对象修改会怎么样呢?看下面这个例子:

//People类和上一个例子中的一样,这里不再列出了。
public static void main(String[] args) { 
   
    List<People> list = new ArrayList<>();
    list.add(new People("小明"));
    list.add(new People("小王"));
    Object[] objects1 = list.toArray();
    Object[] objects2 = list.toArray();
    System.out.println("objects1 == objects2 : "+(objects1 == objects2));
    ((People)objects1[1]).name = "小花";
    System.out.println("show objects1: "+ Arrays.toString(objects1));
    System.out.println("show objects2: "+ Arrays.toString(objects2));
    System.out.println("show list: "+list);
}

输出结果:

objects1 == objects2 : false
show objects1: [People{name=‘小明’}, People{name=‘小花’}]
show objects2: [People{name=‘小明’}, People{name=‘小花’}]
show list: [People{name=‘小明’}, People{name=‘小花’}]

从这个例子的输出结果可以看出,对元素对象本身就行修改,会导致toArray()返回的所有数组中的内容都发生改变,包括原始的list容器里面的元素类容。从这个例子可以得出,如果list.toArray()返回的数组中存放的是list原始对象的引用,只是创建了一个新的数组来装这些引用,并没有对list中原始对象进行拷贝或复制。

都已经分析到这儿了,再看一下Java内部实现的源码。首先看一下List中toArray()方法的申明:


 /** * Returns an array containing all of the elements in this list in proper * sequence (from first to last element). * * <p>The returned array will be "safe" in that no references to it are * maintained by this list. (In other words, this method must * allocate a new array even if this list is backed by an array). * The caller is thus free to modify the returned array. * * <p>This method acts as bridge between array-based and collection-based * APIs. * * @return an array containing all of the elements in this list in proper * sequence * @see Arrays#asList(Object[]) */
    Object[] toArray();

这只是一个申明,由于我们在上面的例子中使用的是ArrayList这个实现类,接下来我们再看一下ArrayList中的实现源码:

/** * Returns an array containing all of the elements in this list * in proper sequence (from first to last element). * * <p>The returned array will be "safe" in that no references to it are * maintained by this list. (In other words, this method must allocate * a new array). The caller is thus free to modify the returned array. * * <p>This method acts as bridge between array-based and collection-based * APIs. * * @return an array containing all of the elements in this list in * proper sequence */
public Object[] toArray() { 
   
    return Arrays.copyOf(elementData, size);
}

可以看到,在ArrayList中的实现是调用了Arrays工具类的copyOf()方法,这和ArrayLIst类中元素的存储结构相关,具体的细节就不在这里进行分析了,如果有时间的话,以后会对ArrayList这个常用的类进行一个分析。至于Arrays.copyOf()方法的作用就是上面分析的List的toArray()方法的作用了,因为toArray()本质上就是直接调用的Arrays.copyOf()方法了。

下面再来分析一下List的toArray(T[] a)方法。

toArray(T[] a)方法的分析

先看一下Java8中Api对于toArray(T[] a)方法的描述:

<T> T[] toArray(T[] a)

Returns an array containing all of the elements in this list in proper sequence (from first to last element); the runtime type of the returned array is that of the specified array. If the list fits in the specified array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the specified array and the size of this list.

If the list fits in the specified array with room to spare (i.e., the array has more elements than the list), the element in the array immediately following the end of the list is set to null. (This is useful in determining the length of the list only if the caller knows that the list does not contain any null elements.)

Like the toArray() method, this method acts as bridge between array-based and collection-based APIs. Further, this method allows precise control over the runtime type of the output array, and may, under certain circumstances, be used to save allocation costs.

Suppose x is a list known to contain only strings. The following code can be used to dump the list into a newly allocated array of String:

    String[] y = x.toArray(new String[0]);

Note that toArray(new Object[0]) is identical in function to toArray()

  • Specified by:

toArray in interface Collection<E>

  • Type Parameters:

T – the runtime type of the array to contain the collection

  • Parameters:

a – the array into which the elements of this list are to be stored, if it is big enough; otherwise, a new array of the same runtime type is allocated for this purpose.

  • Returns:

an array containing the elements of this list

  • Throws:

ArrayStoreException – if the runtime type of the specified array is not a supertype of the runtime type of every element in this list

NullPointerException – if the specified array is null

看着有点长哈,我在这儿进行一个简要的描述,toArray(T[] a)方法使用了泛型参数,可以返回指定类型数组,但是这个泛型在确定的时候必须是list中元素类型的父类或本身,至于那个参数数组,其实就是为了传递参数类型罢了,在给出的例子中:

  String[] y = x.toArray(new String[0]);

可以清楚的看到,传进去的就是一个String 的空数组,然后返回的是list中String类型元素组成的数组。并且API中还提到,如果传进去的是new Object[]类型空数组,那么toArray(T[] a)方法就和toArray()方法相同了,我在这里猜测toArray(T[] a)方法的实现和toArray()方法应该是一样的,就不看list中的接口了,直接看ArrayList中的实现源码:

/** * Returns an array containing all of the elements in this list in proper * sequence (from first to last element); the runtime type of the returned * array is that of the specified array. If the list fits in the * specified array, it is returned therein. Otherwise, a new array is * allocated with the runtime type of the specified array and the size of * this list. * * <p>If the list fits in the specified array with room to spare * (i.e., the array has more elements than the list), the element in * the array immediately following the end of the collection is set to * <tt>null</tt>. (This is useful in determining the length of the * list <i>only</i> if the caller knows that the list does not contain * any null elements.) * * @param a the array into which the elements of the list are to * be stored, if it is big enough; otherwise, a new array of the * same runtime type is allocated for this purpose. * @return an array containing the elements of the list * @throws ArrayStoreException if the runtime type of the specified array * is not a supertype of the runtime type of every element in * this list * @throws NullPointerException if the specified array is null */
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) { 
   
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

注释有点长,比较的详尽,暂时先不管注释,直接看实现的源码。从实现源码中看出,首先会判断数组a的长度和list元素的个数,进行一个比较,如果a数组的长度小于list元素个数,那么就会直接调用工具类Arrays.copyOf()方法直接进行一个拷贝,注意,这儿的是Arrays.copyOf(elementData, size, a.getClass())方法,是由元素类型参数的,就是最后一项参数,和toArray()方法实现Arrays.copyOf(elementData, size)不相同。

接着在看,如果数组a的长度length不小于list元素个数,即 a . l e n g t h &gt; = s i z e a.length &gt;= size a.length>=size得话,就会走下面的流程,首先调用System.arraycopy(elementData, 0, a, 0, size)将ArrayList里的元素数组elementData中的元素拷贝到a对象中,至于这个System.arraycopy()在这里就不详细说了。

接下来会判断一下a.length 是否大于list元素个数size,如果大于的话,会在a[size]位置设置一个null,这个设置的目的是了toArray(T[] a)方法调用者从返回的数组中检测到null时就知道后面已经没有list元素对象了。

终于分析完了,ArrayList的toArray(T[] a)源码代码证明自己上面的猜测是错的,toArray(T[] a)的实现和toArray()方法的实现很不一样,并且其实List的toArray(T[] a)方法的API说明中也提到了,如果传递进来的数组a的空间是大于list的话,就不是直接生成一个新数组来装list原来的元素对象了,而是往a数组中填,并且在最后还是放置一个null,并且在api中也说明了放置null的作用。

看来自己看的还是不够仔细,观察力还有待提升啊。同时注意个小细节,ArrayList中对于toArray(T[] a)方法的注释只是一个简要的,List中对于toArray(T[] a)方法的注释更为详尽,并且还举了例子,以后看方法注解的时候还是尽量看详尽的那一个,免得遗漏信息。

下面对这两个方法进行一个小的总结吧。

总结

List接口的toArray()方法就是直接调用Arrays.copyOf(elementData, size),将list中的元素对象的引用装在一个新的生成数组中。

List接口的toArray(T[] a)方法会返回指定类型(必须为list元素类型的父类或本身)的数组对象,如果a.length小于list元素个数就直接调用Arrays的copyOf()方法进行拷贝并且返回新数组对象,新数组中也是装的list元素对象的引用,否则先调用System.arraycopy()将list元素对象的引用装在a数组中,如果a数组还有剩余的空间,则在a[size]放置一个null,size就是list中元素的个数,这个null值可以使得toArray(T[] a)方法调用者可以判断null后面已经没有list元素了。

好了,终于分析完了!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/146278.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 思科自研芯片_新思科技是大公司吗

    思科自研芯片_新思科技是大公司吗https://www.toutiao.com/a6673484789430878728/3月15日,上海,由智东西主办、AWE和极果联合主办的GTIC2019全球AI芯片创新峰会成功举办!峰会现场延续上一届的火爆场景,全场从开幕到下午结束座无虚席,而且有不少热情观众坚持站着听完峰会全程。20位海内外AI芯片业界大咖齐聚一堂,围绕AI芯片在架构创新、生态构建、场景…

  • 个人能不能开发ctp期货交易_什么是程序化交易期货

    个人能不能开发ctp期货交易_什么是程序化交易期货接触CTP也才半年多,一边学习一边摸索,看到各大CTP的QQ群里,也都是在问一些很菜的问题,就简单总结和介绍下,今天主要是基础知识,即CTP程序的基础和开源的Demo版本:CTP交易接口是由::::::上海期货信息技术有限公司::::::开发的,提供C++的接口,网上也有很多C++的Demo版本,可以直接使用。1:上期所的接口为两个.dll、两个.lib和四个.h文件,初学者可以不要C

  • FTP命令 上传下载文件[通俗易懂]

    FTP命令 上传下载文件[通俗易懂]1.连接ftp服务器格式:ftp[hostname|ip-address]a)在linux命令行下输入:ftp192.168.1.1b)服务器询问你用户名和密码,分别输入用户名和相应密码,待认证通过即可。2.下载文件下载文件通常用get和mget这两条命令。a)get格式:get[remote-file][local-file]将文件从远端主机中传送至…

  • 【边做项目边学Android】小白会遇到的问题–This Android SDK requires Android Developer Toolkit version 23.0.0 or above

    【边做项目边学Android】小白会遇到的问题–This Android SDK requires Android Developer Toolkit version 23.0.0 or above

  • MPEG-2_mpeg编码模式包括

    MPEG-2_mpeg编码模式包括作者:haibara来源:pcicp.com本FAQ由(haibara)翻译,期间受到kaito_mkid(pcicp)帮助,在此感谢,由于Newbie的关系,如有翻译错误,还请各位指出,由Doom9的bond编撰的原文。译者注:基本专业名称我都以小括号形式加以解释,详细信息请自行查阅资料。什么是MPEG-4?MPEG-4(ISO14496)是由制定了我们熟知的MPEG-1(MP3,…

  • Windows服务器IIS搭建网站

    Windows服务器IIS搭建网站概述:以下总结了学习IIS搭建网站的方法,先从基本使用IIS搭建网站,到本地使用域名访问网站,再配置路由器实现外网IP访问内网网站,以及使用动态域名外网访问内网网站,最后配置不同域名(IIS也叫主机名),相同IP,相同端口(80)的不同的网站的访问(注意域名不备案外网无法访问)!即输入以下域名可以访问相同端口(80)但不同的网站:127.0.0.1/test1——&gt;www.g…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号