深入理解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;}@Overridepublic 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.length&gt;=sizea.length &gt;= sizea.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元素了。

好了,终于分析完了!

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

  1. [Java 基础] 深入理解List的toArray()方法和toArray(T[] a)方法

    深入理解List的toArray()方法和toArray(T[] a)方法 这两个方法都是将列表List中的元素转导出为数组,不同的是,toArray()方法导出的是Object类型数组,而toArr ...

  2. 详解List的toArray()方法和toArray(T[] a)方法

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

  3. List的toArray()方法和toArray(T[] a)方法

    1.toArray()方法 会返回List中所有元素构成的数组,并且数组类型是Object[].还要注意一点就是,返回的数组是新生成的一个数组, 也就是说,多次运行toArray()方法会获得不同的数 ...

  4. jqgrid和ajax的区别,理解jqGrid的添加方法和Ajax请求

    我有一个现有的项目,我需要维护,但是这是我第一次满足的jqGrid ...理解jqGrid的添加方法和Ajax请求 主要是,我有一个Product,即可以有多个Formule小号.每个Formule可 ...

  5. Node.js的环境搭建方法和 npm 的使用方法

    Node.js的环境搭建方法和 npm 的使用方法 Node.js 环境的搭建 Node.js 介绍 Node.js 下载 配置Node.js node.js 测试 npm(包管理器)使用方法 npm ...

  6. 联想集群超算LICO初次使用踩坑说明(遇到的错误,一些使用方法和singularity新建容器方法)

    联想集群超算LICO初次使用踩坑说明(遇到的错误,一些使用方法和singularity新建容器方法) 说明 本文主要写一些我这几天初次使用LICO的过程中遇到的一点问题和解决的办法,还有一些模块的规范 ...

  7. JavaSE学习之路:Idea小技巧一键生成标准JavaBean(一键生成构造方法和Setter和Getter方法)

    Idea小技巧一键生成标准JavaBean(一键生成构造方法和Setter和Getter方法) 方法1:快捷键 快捷键:alt+insert或alt+fn+insert 1.生成有参和无参构造函数 p ...

  8. CString的Trim()方法和TrimStrat()和TrimEnd()方法,TrimRight()和TrimLeft() 方法

    一. CString的Trim()方法和TrimStrat()和TrimEnd()方法.这三个方法用于删除字符串头尾出现的某些字符. Trim()删除字符串头部及尾部出现的空格,删除的过程为从外到内, ...

  9. View的生命周期方法和Activity生命周期方法关系

    View 是在Activity 中使用到的,所以在自定义View的时候,我们需要了解Activity 生命周期方法和View的生命周期方法调用先后顺序.  见如下图 (1) 在Activity onC ...

最新文章

  1. 树和二叉树(四种遍历,建树)详解+二叉排序树(包含图像和相关习题)
  2. 使用VC++6.0创建MFC对话框程序
  3. 升压转换器 (Boost)
  4. 【转】java线程系列---Runnable和Thread的区别
  5. 使用curl自动签到smzdm
  6. SqlConnection中 Close()Dispose()Using()的区别
  7. C#图形处理系列(一)——最简单第一步:逆反处理、二值处理
  8. 洛谷P1204 [USACO1.2]挤牛奶Milking Cows 前缀和
  9. Vue cli3+Hubuilder将项目打包为App
  10. egg框架访问 Mysql 数据库 egg-mysql 增删改查
  11. 利用pandas,pyplot 绘制柱状图
  12. linux文件压缩包,linux目录文件压缩包操作
  13. Sharding-JDBC读写分离_MySql主从配置(1)_Sharding-Sphere,Sharding-JDBC分布式_分库分表工作笔记015
  14. html显示作者信息,HTML基础 meta name author 添加网页作者的信息
  15. webpack 介绍 安装 常用命令
  16. ubuntu14.04 配置C/C++开发IDE
  17. 2.1.6 用ProtectX实现扫描的反击与追踪
  18. 毕业论文免费查重软件测评及降重心得
  19. 基于STM32单片机的智能药盒定时提示吃药喂水蓝牙APP方案原理图设计
  20. 如何关闭笔记本电脑触摸板功能

热门文章

  1. 基于“认知预期”的情境意识预测模型
  2. 前端实现网页选区截图功能
  3. 华为鸿蒙卖了吗,华为鸿蒙新品来了!卖3000多块钱
  4. 小米11pro和小米11ultra哪个好
  5. .Net -- AutoMapper简单使用
  6. 我的世界中国版服务器家园系统,《我的世界》中国版家园建造图文教程 家园建造位置推荐...
  7. Oracle数据库备份迁移
  8. jedis操作set_jedis 操作 -set集合
  9. Ruby七日谈.第二夜—面向对象
  10. 功能设计:给淘宝设计一个多人拼单购买的新功能