Java 集合转数组问题, list.toArray(T[] a)

在List转数组时,一般会这么写

List<String> list = Arrays.asList("a","b","c");
// 方式1
list.toArray(new String[0]);
// 方式2
list.toArray(new String[list.size()]);

下面是jdk源码(出自ArrayList.toArray(T[] a)

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

可以看到当入参数组的长度小于List时,创建了一个新的数组,而如果入参数组大小大于等于List,则直接将List的元素拷贝到入参数组中。

为何不这样实现:

public <T> T[] toArray(Class<T[]> type) {
    return Arrays.copyOf(elementData, size, type);
}

代码简单,调用方法时也不用去new一个数组实例?
话说回来,那List转数组的入参中,数组大小到底是设置0还是list.size()呢?

阅读 5.1k
2 个回答

2019-12-29 更新一下答案

stackoverflow问了一下国外的大佬们,针对我之前的疑问,有了以下回复(问题链接

  1. 题主提到的方案,可以么
    答案是可行的,方法本身是可行的,如果觉得有需要,可以提一个增强的请求给API设计人员,如果有很多人都想要这个新方法的话,是可以实现的
    image.png

    再提到为啥toArray(T[] a)是需要传一个泛型的数组,是因为toArray方法,本身早于泛型,再后续的最早之前的方法就是传入一个Object数组,类似这样public Object[] toArray(Object[] a),所以当Java5泛型出来后,这些方法就被改成泛型的方法了,有一定的历史原因

  2. 至于我提到的疑问,虽然我在那个stackoverflow问题上没有提到,但是大佬在讲解历史的时候有提到点这个,嘤语水平有限,大致意义感觉是设计者参考以前C语言的处理怪癖,注意,这里用到了一个词语quirk(苦笑)...

    image.png

好吧,总之是大佬可以为所欲为~


看到题主的评论,我发现我之前有一部分想错了,没想到可以String[].class这么写,我之前确实从来没有这么写过,更不会想到去传XXObject[].class,毕竟Java8里的stream API里也有个toArray方法,也不是传递XXObject[].class,而是传递一个数组的构造方法,看了源码,最后也是去根据当前stream大小创建的新数组。。。

因此题主的想法还是很有意思哈,貌似就没有见到jdk已有API传数组class的,除了Arrays里有几个方法,那现在为止就是2个问题了

  1. 跟题主一样的疑问
  2. 就是我之前回答的疑问,为啥toArray里有个置空,但是只是置空集合最后一位之后那个数组位置的值

以下是原答案,思考有些错误,就灰掉把


首先更正一个题主一个小问题哈
题主提到想要这样实现:
![image.png](/img/bVbB4kd)

其实这里应该是这样才可以调用的


public <T> T[] toArray(Class<? extends T[]> type) {  
    return Arrays.copyOf(elementData, size, type);  
}

而且就算是这样的话,请问题主怎么传递这个`type`呢?不知道题主试过没有,如果尝试去调用一下,就会发现如果真这么写,也很奇怪,会与题主的本意冲突

为啥呢?

题主本意其实是希望说`List`转换成`Array`时,只用传一个`Array`里的具体类型的数组`class`就行了,因此就没必要在外面再创建一个数组了(而且数组大小怎么定,是`0`还是`list.size()`)

但这真按照题主的想法`Class<? extends T[]> type`作为参数传值时,其实会这样去调用

Integer[] arr = new Integer[0];  
Class<? extends Integer[]> aClass = arr.getClass();  
Integer[] integers = list.toArray(aClass);

没错!!!,其实还是要创建一个新的数组,尴尬的雅痞~哈哈哈

不过也别觉得奇怪,咱们捋捋,你说如果你想要传递一个指定类型数组的`class`,是不是就要创建一个这个类型的数组,因为你指定类型的数组都是长这样,`String[]`或者`XXObject[]`,如果你要取类型,就必须创建了数组再`getClass`,这个可不像普通的类,你可以`XXObject.class`,所以就不能想成写做`XXObject[].class`

所以别人设计这个`API`,让你少调用一次`arr.getClass()`,它不香么~~~

而且题主想要这种实现,其实只是考虑了`List`转换成一个`Array`(也许是大多数情况),但是没有考虑本身有一个`Array`然后希望去接收`List`里的值这种情况

那别人合二为一的,并且参数还得统一,都是传一个数组,这个数组
1. 既指定了数组的类型
2. 又可以成为本身接受`List`里值的容器

它哪点不香~

最后,题主还问了,数组大小到底是传`0`还是`list.size()`?
根据之前对于这个方法的分析,那这不很明显了么?你是那种情况,你就怎么调用啊~
因为你只是想要把`List`转换为`Array`,那就需要指定数组类型,当然数组大小传`0`是可以的,传`list.size()`也行,就表示你准备好了一个`Array`了,直接把`List`里的值放进来就可以了

以上是我个人理解
(不过说句题外话,其实看了`toArray(T[] a)`的源码之后,反而对最后`if (a.length > size)`的处理有点迷惑,没整明白这里的操作,为啥要把`Array`里紧跟着`List`的最后元素置为空,这是什么窒息的操作,从方法注释上也没整明白
![image.png](/img/bVbB4mL)

最后括号里的话翻译过来是:只有当调用者知道列表不包含任何空元素时,这才有助于确定列表的长度
没怎么懂。。。不理解是什么场景下的处理,谁看到可以讨论一下~~
)

如果你传入的数组大小不够怎么办?可以自己试下,这种情况就抛异常了啊。如果你已经有一个分配好的大小合适的数组,那就可以直接传进去,如果没有那就用0好了。

书中举的例子大部分并不是实际的应用场景,需要自己在开发中多多体会。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏