集合数组互转

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表示集合中元素个数

image-20220818194834714

点进去,继续调用copyOf方法

image-20220818194858746

点进去,最后调用了java.lang.System.arraycopy方法

image-20220818195009733

可以看到,由于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

image-20220818200230030

当length >= size,则直接调用System.arraycopy方法,将原数组元素复制到a数组内。其中,当length > size,则将多余的位置赋null

image-20220818200638008

总结

根据上面的分析,假设对一个长度为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 ,下面对该方法简单解释下

源码

image-20220818201313782

arraycopy方法可以将一个数组的部分元素移动到另一个数组。

参数说明

该方法共有五个参数,含义如下

参数 描述
src 原数组
srcPos 原数组待复制的起始索引
dest 目标数组
destPos 复制到目标数组的起始索引
length 需要复制的元素个数

验证

1
2
3
4
5
6
7
8
public static void test() {
int[] array1 = {1,2,3};
int[] array2 = {0,0,0,0,0,0};

System.arraycopy(array1, 0, array2, 3, 2);

System.out.println(Arrays.toString(array2));// [0, 0, 0, 1, 2, 0]
}

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
2
int[] array2 = {1, 2, 3};
List<int[]> list2 = Arrays.asList(array2);
可变参数

可变参数可以为包装类型

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部分源码

注意:Arrays.ArrayList虽然实现了java.util.AbstractList,但没有重写set, add, remove等方法

Arrays.ArrayList未重写set, add, remove方法

AbstractList中remove方法

因此在调用set, add, remove方法时,不会编译报错,但运行时会抛java.lang.UnsupportedOperationException异常

即:通过asList得到的集合,是不可变集合,不支持set, add, remove操作


Collections.addAll(推荐)

由以上分析看出,Arrays.asList虽然可以很方便的将数组转换成集合,但无法对该集合进行增加、删除等操作。因此最好的办法是将数组转换成一个可变集合。Collections.addAll方法通过将数组加入到一个长度为0的可变集合中,变相实现了数组转集合的需求。

用法

接收一个集合数组(或可变参数),可以实现将数组(或可变参数)加入到集合中。

数组

要求数组类型必须为包装类型

1
2
3
String[] arr = new String[]{"aa", "bb", "cc"};
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, arr);

数组类型如果为基本类型,则无法通过编译

1
2
int[] arr = new int[]{1,2,3};
Collections.addAll(list, arr);
可变参数

可变参数可以为包装类型

1
2
ArrayList<String> list1 = new ArrayList<>();
Collections.addAll(list1, "dd", "ee", "ff");

也可以为基本类型(此时会自动装箱)

1
2
ArrayList<Integer> list2 = new ArrayList<>();
Collections.addAll(list2, 1,2,3);

源码分析

内部循环调用了java.util.Collection.add方法,可见如果addAll传入Arrays.ArrayList类型也是不被支持的

image-20220822112134432


打印数组

打印数组不需要将数组先转成集合,只需调用 Arrays.toString(Object[] a)方法

用法

1
System.out.println(Arrays.toString(array));

源码

支持八种基本数据类型及Object类型

image-20220822141629337

就是遍历数组然后以逗号拼接

以int数组为例

文章作者: SongGT
文章链接: http://www.songguangtao.xyz/2022/07/29/2.集合数组互转/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 SongGuangtao's Blog
大哥大嫂[微信打赏]
过年好[支付宝打赏]