Java List转数组几种方式性能问题
程序员文章站
2022-07-13 22:53:12
...
List集合使用toArray方法转成数组,该方法重载了两种形式,无参的和有参的,有参的又可以指定目的数组的不同长度,根据数组长度和原有List元素个数(size)的比较又可以分为几种情况,每一种方式对性能的影响不一。本文主要以实例结合源码来分析不同情况性能问题,供实际工作和面试时参考。不多说,直接上干货
创建一个简单的List,插入3个元素
List<String> list1 = new ArrayList<>();
list1.add("a");
list1.add("b");
list1.add("c");
方式1:使用无参toArray方法
Object[] arr1 = list1.toArray();
System.out.println("遍历arr1");
for (Object object : arr1) {
//可能会有类型转换错误
Integer i1 = (Integer)object;
System.out.println(i1);
System.out.println(object);
}
该方式只能转成Object类型数组,原有集合指定的泛型消失,相当于类型擦除
隐患:如果拆箱时强转成其他类型,可能会出现ClassCastException,比如代码中的
Integer i1=(Integer)object 而且这种问题编译时不能识别,把问题隐藏到运行时
方式2:使用有参toArray方法
public <T> T[] toArray(T[] a) {
** 该重载形式可以返回同List对应的泛型数组,保留了元素类型信息,避免了方式1的ClassCastException隐患
方式2.1 目的数组长度大于 0小于原List的 size
String[] arr3 = new String[2];
//长度不够,需要复制一个新数组
arr3 = list1.toArray(arr3);
System.out.println("遍历arr3");
for (String string : arr3) {
System.out.println(string);
}
** 由于数组长度小于List长度,为了能把集合中的元素放入数组,这种方式会重新创建大小等于 size 的数组(复制一个长度为size的新数组),频繁操作会增加 GC 负担。其对应源码为:**
if (a.length < size)
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
方式2.2 目的数组长度大于 0大于原List的 size
String[] arr4 = new String[5];
//目的数组长度大于原List长度
list1.toArray(arr4);
System.out.println("遍历arr4");
for (String string : arr4) {
System.out.println(string);
}
由于数组长度小于List长度,一方面浪费空间,另一方面List size 对应的位置只能插入 null 值,存在 Null指针异常 隐患。其对应源码为:
if (a.length > size)
a[size] = null;
方式2.3 目的数组长度等于 0,动态创建与原List size 相同长度的数组
String[] arr2 = list1.toArray(new String[0]);
System.out.println("遍历arr2");
for (String string : arr2) {
System.out.println(string);
}
不多不少,直接挨个复制,这是性能最好的方式。其对应源码为:
System.arraycopy(elementData, 0, a, 0, size);
结语:即便是很常见,看起来很简单的集合操作,不同的实现方式也会有细微的差别,并且可能在生产环境中会对性能产生不小的影响