前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >详解List的toArray()方法和toArray(T[] a)方法

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

作者头像
全栈程序员站长
发布于 2022-08-28 01:45:37
发布于 2022-08-28 01:45:37
70900
代码可运行
举报
运行总次数:0
代码可运行

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

这两个方法都是将列表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
  • 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不会维持一个对该返回的数组的引用。下面我们做一个小实验来验证一下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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,如果换成其他的自定义类型呢,结果会是怎么样?接下来我们看下面这个例子。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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本身。

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//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()方法的申明:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 /** * 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中的实现源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/** * 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 theelements 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
  • 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中元素类型的父类或本身,至于那个参数数组,其实就是为了传递参数类型罢了,在给出的例子中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String[] y = x.toArray(new String[0]);

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/** * 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 >= 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元素了。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/146636.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年5月1,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
toArray方法总结
Java标准库中Collection接口定义了toArray方法,如果传入参数为空,则返回Object[]数组,如果传入参数为T[],则返回参数为传入参数的运行时类型。以下是ArrayList的实现:
全栈程序员站长
2022/08/28
3590
toArray方法总结
ArrayList 分析以及相关方法介绍
java.util.ArrayList 是我们最常用的一个类,ArrayList 底层是动态数组,读者可以把它理解为数组的实现
cxuan
2019/06/03
5150
数组与List的互转及原理分析
使用asList得到的是一个固定程度的一个list,对新的list做元素的修改时可以的,但是,如果更改了大小就会报异常.
用针戳左手中指指头
2021/01/29
5930
数组与List的互转及原理分析
ArrayList的实现原理浅析
查看ArrayList的源码,发现其继承自AbstractList,实现了List,RandomAccess,Cloneable以及Serializable接口,如:
孟君
2020/04/22
4830
ArrayList源码学习
重点了解:数组为EMPTY_ELEMENTDATA就走基于用户设置大小值进行1.5倍扩容(这里是空所以是0),数组为默认空DEFAULTCAPACITY_EMPTY_ELEMENTDATA就会走基于默认值的大小10扩容进行1.5倍扩容
晓果冻
2022/09/08
3870
ArrayList源码学习
Java中List与Array的转换
    在Java项目开发过程中,集合之间的互相转换是非常常见的,其中两个比较典型的转换是List和Array之间的转换,本文主要介绍这二者之间的转换、其中存在的一些问题以及解决方案,本文JDK版本为1.8。
Tyan
2022/05/09
4620
Java数组 强制类型转换
最重要的是!!!最开始的时候声明的数组类型!!! 最重要的是!!!最开始的时候声明的数组类型!!! 最重要的是!!!最开始的时候声明的数组类型!!!
地鼠窝里有个Gopher
2022/10/30
1.9K0
你连存活到JDK8中著名的Bug都不知道,我怎么敢给你加薪
SubClass 继承自BaseClass,由于SubClass数组中每一个元素都是SubClass对象,所以BaseClass[] baseArray = subArray;这种强制类型转换不会报错。这其实就是java对象的向上转型,子类数组转换成父类数组是允许的。但是由于数组中元素类型都是SubClass类型的,所以 baseArray[0] = new BaseClass();会报错java.lang.ArrayStoreException。这也就是说假如我们有1个Object[]数组,并不代表着我们可以将Object对象存进去,这取决于数组中元素实际的类型。
JavaEdge
2020/03/12
8250
ArrayList详解
ArrayList 是一个数组列表。它的主要底层实现是Object数组,但与 Java 中的数组相比,它的容量能动态变化,可看作是一个动态数组结构。特别注意的是,当我们装载的是基本类型的数据 int,long,boolean,short,byte… 的时候,我们只能存储他们对应的包装类。
程序员阿杜
2023/08/25
2500
这可能是最细的ArrayList详解了!
# 手撕ArrayList源码 > 文章首发于GitHub开源项目: [Java超神之路](https://github.com/shaoxiongdu/java-notes) ## ArrayList 简介 ArrayList 是一个数组列表。它的主要底层实现是`Object`数组,但与 Java 中的数组相比,它的**容量能动态变化**,可看作是一个动态数组结构。特别注意的是,当我们装载的是基本类型的数据 int,long,boolean,short,byte… 的时候,我们只能存储他们对应的包装
程序员阿杜
2021/09/11
9440
JDK源码分析-ArrayList分析
花了两个晚上的时间研究了一下ArrayList的源码, ArrayList 继承自AbstractList 并且实现了List, RandomAccess, Cloneable, Serializable 通过实现这三个接口 就具备了他们的功能 RandomAccess 用来表明其支持快速(通常是固定时间)随机访问 Cloneable可以克隆对象 Serializable 对象序列化就是把一个对象变为二进制的数据流的一种方法,通过对象序列化可以方便地实现对象的传输和存储,Seriali
汤高
2018/01/11
1.7K0
JDK源码分析-ArrayList分析
Java面试题之List(一)
我们都知道,Java面试中避免不了的要涉及到对各种框架和源码的盘问。有的面试官会直接点,问我们对ArrayList的源码的理解,有的会间接的提问。
黑洞代码
2021/01/14
4350
Java ArrayList集合常用方法
创建对象:与其他普通的引用数据类型创建方式完全相同,但要指定容器中存储的数据类型:
用户7886150
2020/12/12
3300
java.util.AbstractCollection[源码解读]
AbstractCollection抽象类继承自Collection接口,它提供了对Collection接口的基本实现,从而使得实现Collection接口的成本最小化。
小诸葛
2020/04/14
4810
Java Array、List、Set互相转化
大家好,又见面了,我是你们的朋友全栈君。 Java ArrayListSet互相转化 ArrayListSet互转实例 1 ArrayList互转 2 ListSet互转 3 ArraySet互转 ArraysasList 和 CollectiontoArray Java Array、List、Set互相转化 1. Array、List、Set互转实例 1.1 Array、List互转 1.2 List、Set互转 因为List和Set都实现了Collection接口,且addAll(Collec
全栈程序员站长
2022/06/24
3250
ArrayList源码解析,老哥,来一起复习一哈?
JDK源码解析系列文章,都是基于JDK8分析的,虽然JDK14已经出来,但是JDK8我还不会,我…
敖丙
2020/05/26
6440
ArrayList源码解析,老哥,来一起复习一哈?
3秒搞定ArrayList
底层是一个Object[],添加到ArrayList中的数据保存在了elementData属性中。
老兵程序员
2021/06/15
4950
ArrayList 源码笔记
每次添加时modCount都会自增,modCount在迭代器开始迭代时会被记录,除非通过迭代器自身的 remove 或 add 方法从结构上对列表进行修改,否则迭代过程中modCount发生改变,就会抛出 ConcurrentModificationException 异常。
MashiroT
2022/10/28
2040
【Java入门提高篇】Day21 容器类详解(四)ArrayList源码分析
   今天要介绍的是List接口中最常用的实现类——ArrayList,本篇的源码分析基于JDK8,如果有不一致的地方,可先切换到JDK8后再进行操作。
弗兰克的猫
2018/06/03
7430
【Java入门提高篇】Day21 容器类详解(四)ArrayList源码分析
ArrayList 其实也有双胞胎,但区别还是挺大的!
这句话的意思是Collection集合类型的toArray()方法虽然声明返回值类型是Object[],但是具体调用时还真不一定就返回Onject[]类型,也有可能是其他的类型,这还要取决于你c的实际类型,使用不当还会抛出异常。这样讲可能会很懵比,下面我将会详细讲解到底为什么,现在我们先来看看Collection中的toArray()声明,让你对这个方法先有个大概的印象。
芋道源码
2018/10/26
5210
相关推荐
toArray方法总结
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验