首页 > 编程语言 >Java里ArrayList中的toArray()用法

Java里ArrayList中的toArray()用法

时间:2024-01-19 10:12:44浏览次数:26  
标签:toArray Java objects1 People ArrayList list 数组 array

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

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

toArray()方法的分析
Object[] toArray()
1
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

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 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 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 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).
*
*

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.
*
* @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).
  • 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.
  • 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[] 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]);

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

Specified by:
toArray in interface Collection

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]);
1
可以清楚的看到,传进去的就是一个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.
  • 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
  • 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.)
  • @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[] 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 > = s i z e a.length >= 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元素了。
————————————————
版权声明:本文为CSDN博主「拧螺丝专业户」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/mucaoyx/article/details/86005283

标签:toArray,Java,objects1,People,ArrayList,list,数组,array
From: https://www.cnblogs.com/zhouyuejun/p/17974009

相关文章

  • 基于JAVA的新闻类网站
    21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们所认识,科学化的管理,使信息存储达到准确、快速、完善,并能提高工作管理效率,促进其发展。论文主要是对新闻类网站进行了介绍,包括研......
  • 论文翻译 | 【深入挖掘Java技术】「底层原理专题」深入分析一下并发编程之父Doug Lea
    前提介绍DougLea在州立大学奥斯威戈分校(DougLea)摘要本文深入探讨了一个Java框架的设计、实现及其性能。该框架遵循并行编程的理念,通过递归方式将问题分解为多个子任务,并利用工作窃取技术进行并行处理。所有子任务完成后,其结果被整合以形成完整的并行程序。在总体设计上,该框架借鉴......
  • javascript中的undefined可以被重写
    众所周知,当声明一个变量,并且没有给赋值的情况下,它的初始值是 undefined。但是在javascript中,怎么检查一个值是否为 undefined 呢?在现代浏览器中,你可以安全的直接比较将变量是与 undefined 进行比较if(name===undefined){//...}一些人反对直接使用 undefined......
  • 深入理解Java中的ThreadLocal
    第1章:引言大家好,我是小黑。今天咱们来聊聊ThreadLocal。首先,让咱们先搞清楚,ThreadLocal是个什么玩意儿。简单说,ThreadLocal可以让咱们在每个线程中创建一个变量的“私有副本”。这就意味着,每个线程都可以独立地改变自己的副本,而不会影响其他线程。这就像是每个人都有自己的笔记......
  • 【APP逆向09】Java基础之数据类型(Map)
    1.Map是一个接口,常见实现这个接口的有两个类,用于存储键值对。HashMap,无序。TreeMap,默认根据key排序。(常用)2.实例2.1:HashMapimportjava.util.HashMap;publicclassDemo7{publicstaticvoidmain(String[]args){HashMaph1=newHashMap();......
  • Java 线程池
    Java线程池前言:创建过多的线程会占用更多的内存、并且在线程切换的时候增加消耗的资源和浪费更多的时间,为了缓解以上问题,出现一种基于复用和预分配思想的技术,线程池。线程池中的线程被统一创建和管理,提高了系统响应时间和系统的资源利用率。除了线程池解决以上问题外,在java21......
  • 初识Java
    初识Java一场旷日持久的战争故事:Java帝国的诞生C&C++1972年C诞生贴近硬件,运行极快,效率极高。操作系统,编译器,数据库,网络系统等指针和内存管理1982年C++诞生面向对象兼容C图形领域、游戏等反抗我们要建立一个新的语言:语法有点像C没有指......
  • Js(Javascript)的apply call 和bind区别
    ​ apply、call和bind是用于调用函数的三种不同方式,它们的主要区别在于函数调用时的上下文(this关键字)以及参数传递的方式。call和apply是用于立即调用函数并设置this上下文的方法,它们的主要区别在于参数传递的方式。bind不会立即执行函数,而是创建一个新的函数,将this......
  • 《Java解惑》PDF
    本书特写了95个有关Java或其类库的陷阱和缺陷的谜题,其中大多数谜题都采用了短程序的方式,这些程序的行为与其看似的大相径庭。在每个谜题之后都给出了详细的解惑方案,这些解惑方案超越了对程序行为的简单解释,向读者展示了如何一劳永逸地避免底层的陷阱与缺陷。本书趣味十足、寓教于......
  • JAVA碎知识-2
    1、Set在Java中,Set 是一种集合接口(Interface),它继承自 Collection 接口,并且是一个无序、不重复的集合。不重复性:Set 中的元素是唯一的,不允许重复元素。Java中常用的 Set 实现类有以下几种:HashSet:基于哈希表实现,具有较快的插入、删除和查找操作。不保证元素的顺序。Tre......