ArrayList底层使用的就是Object数组,二者的互转也经常用到,但平时很少关注具体实现。本文列举了二者互转的一些方法,并对方法源码进行一些简单的探究。
集合转数组
java.util.ArrayList
提供两个方法Object[] toArray()
和<T> T[] toArray(T[] a)
,可以实现集合转数组。
toArray()
用法
调用无参toArray
方法,返回Object数组
1 | Object[] objectArray = list.toArray(); |
源码分析
toArray()
内部调用了java.util.Arrays.copyOf(T[], int)
方法,传入两个参数。其中
elementData是保存集合元素的Object数组
size表示集合中元素个数
点进去,继续调用copyOf
方法
点进去,最后调用了java.lang.System.arraycopy
方法
可以看到,由于elementData类型为Object[].class,所以该方法会新建一个Object数组赋值给copy,并将elementData中所有元素都复制到copy内,最终返回copy。
所以有copy.length = size <= elementData.length
toArray(T[] a)(推荐)
无参的toArray()
方法虽然能实现集合转数组,但返回的集合都是Object类型。建议使用带参的toArray(T[] a)
方法
用法
传入一个数组,数组长度与原集合长度保持一致,数组类型与原集合类型保持一致,返回一个相同类型的数组
1 | String[] array2 = list.toArray(new String[list.size()]); |
源码分析
toArray
方法首先判断传入数组的长度,当length < size,即传入数组不足以存放集合数据时,会调用java.util.Arrays.copyOf()
方法。
由于newType为T[].class,所以会创建一个元素类型为T,长度为size的新数组,赋给copy。
然后同样调用了java.lang.System.arraycopy
方法,将原数组内元素复制到copy数组内,并返回copy
当length >= size,则直接调用System.arraycopy
方法,将原数组元素复制到a数组内。其中,当length > size,则将多余的位置赋null
总结
根据上面的分析,假设对一个长度为3的集合,调用list.toArray(T[] a)
,当数组a的长度分别为0,3,5时,有如下结果
集合长度 | 传入数组长度 | 返回数组内容 | 返回与传入数组是否为同一个 | 说明 |
---|---|---|---|---|
3 | 0 | [a, b, c] | 否 | 数组内容一致,返回的是重新创建的数组 |
3 | 3 | [a, b, c] | 是 | 数组内容一致,返回的是传入的数组 |
3 | 5 | [a, b, c, null, null] | 是 | 数组内容不一致,多出的位置用null补齐,返回的是传入的数组 |
可见:当传入数组长度 = 集合长度时,返回的是传入的数组,并且内容相同,开销最小
System.arraycopy
由上面分析可以看到,两个方法底层都是调用的 java.lang.System.arraycopy
,下面对该方法简单解释下
源码
arraycopy方法可以将一个数组的部分元素移动到另一个数组。
参数说明
该方法共有五个参数,含义如下
参数 | 描述 |
---|---|
src | 原数组 |
srcPos | 原数组待复制的起始索引 |
dest | 目标数组 |
destPos | 复制到目标数组的起始索引 |
length | 需要复制的元素个数 |
验证
1 | public static void test() { |
array2的内容变为[0, 0, 0, 1, 2, 0],表示将array1从0开始的元素移动到array2从3开始的位置,移动的元素个数是2
数组转集合
java.util
包下两个方法可以实现数组转集合,分别是Arrays.asList(T... a)
和 Collections.addAll(Collection<? super T> c, T... elements)
Arrays.asList
用法
接收数组或可变参数作为参数,返回的是 java.util.Arrays.ArrayList
类型。
数组
要求数组类型必须为包装类型
1 | List<String> list = Arrays.asList(array); |
如果数组为基本类型,无法自动装箱,导致得到的集合泛型为int[]
类型
1 | int[] array2 = {1, 2, 3}; |
可变参数
可变参数可以为包装类型
1 | List<String> list = Arrays.asList("dd", "ee", "ff"); |
也可以为基本类型(此时会自动装箱)
1 | List<Integer> list2 = Arrays.asList(1, 2, 3); |
源码分析
asList
接受一个可变数组,返回一个内部类Arrays.ArrayList
的实例,将其中数组a指向了传入数组array
注意:Arrays.ArrayList
虽然实现了java.util.AbstractList
,但没有重写set
, add
, remove
等方法
因此在调用set
, add
, remove
方法时,不会编译报错,但运行时会抛java.lang.UnsupportedOperationException
异常
即:通过asList
得到的集合,是不可变集合,不支持set
, add
, remove
操作
Collections.addAll(推荐)
由以上分析看出,Arrays.asList
虽然可以很方便的将数组转换成集合,但无法对该集合进行增加、删除等操作。因此最好的办法是将数组转换成一个可变集合。Collections.addAll
方法通过将数组加入到一个长度为0的可变集合中,变相实现了数组转集合的需求。
用法
接收一个集合和数组(或可变参数),可以实现将数组(或可变参数)加入到集合中。
数组
要求数组类型必须为包装类型
1 | String[] arr = new String[]{"aa", "bb", "cc"}; |
数组类型如果为基本类型,则无法通过编译
1 | int[] arr = new int[]{1,2,3}; |
可变参数
可变参数可以为包装类型
1 | ArrayList<String> list1 = new ArrayList<>(); |
也可以为基本类型(此时会自动装箱)
1 | ArrayList<Integer> list2 = new ArrayList<>(); |
源码分析
内部循环调用了java.util.Collection.add
方法,可见如果addAll
传入Arrays.ArrayList
类型也是不被支持的
打印数组
打印数组不需要将数组先转成集合,只需调用 Arrays.toString(Object[] a)
方法
用法
1 | System.out.println(Arrays.toString(array)); |
源码
支持八种基本数据类型及Object类型
就是遍历数组然后以逗号拼接