本文目的是从源码层面分清楚 List
中这两个函数的区别:
Object[] toArray()
<T> T[] toArray(T[] a)
。参数 a
由用户传递,用于指定新数组生成的位置。
快速感知
两个函数的作用都是将 ArrayList
对象转换为数组,都返回新数组 ,多次运行 toArray
方法会获得不同的数组对象,但是这些数组对象中内容一样的。
区别在于是否携带参数。具体区别:
Object[] toArray()
中
如果 ArrayList 中的内容是基本类型,你可以简单的认为新数组之间没有任何关联。
如果 ArrayList 中的内容是自定义类型,那么即使 toArray
返回不同的新数组,但是在不同的新数组中,对应下标元素均引用同一个对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 private static class MyPair { int val; public MyPair (int val) {this .val = val;} @Override public String toString () { return "MyPair{ val = " + this .val + '}' ; } } public static void main (String[] args) { List<MyPair> ls =new ArrayList <>(); ls.add(new MyPair (0 )); ls.add(new MyPair (1 )); ls.add(new MyPair (2 )); Object[] newArr1 = ls.toArray(); Object[] newArr2 = ls.toArray(); System.out.println(newArr1==newArr2); System.out.println(newArr1[0 ]==newArr2[0 ]); newArr1[0 ] = new MyPair (99 ); System.out.println(Arrays.toString(newArr1)); System.out.println(Arrays.toString(newArr2)); ((MyPair)newArr2[1 ]).val = 66 ; System.out.println(Arrays.toString(newArr1)); System.out.println(Arrays.toString(newArr2)); }
<T> T[] toArray(T[] a)
,a
指的是用户可以指定的新数组的存储位置,返回类型为 T[]
。如果用户给的 a
足够大,代码就会把元素复制到 a
中,如果还有富余,则结尾设置 null
;如果不够大,则代码自己生成一个大小刚好符合要求的新的数组,再复制返回。
ls.toArray(new Object[0])
等同于 ls.toArray()
一、(六)9.【强制】 使用集合转数组的方法, 必须使用集合的 toArray(T[] array)
, 传入的是类型完全一致、 长度为 0 的空数组。
原因:
直接使用 toArray
无参方法存在问题, 此方法返回值只能是 Object[]
类, 若强转其它类型数组将出现 ClassCastException
错误。
使用 toArray 带参方法, 数组空间大小的 length
的选择:
等于 0, 动态创建与 size
相同的数组, 性能最好。
大于 0 但小于 size
, 重新创建大小等于 size
的数组, 增加 GC 负担。也就是说,作为参数传入的那个数组没用上。
等于 size
, 在高并发情况下, 数组创建完成之后, size
正在变大的情况下, 负面影响与第 2 种情况相同。但实际上,并发场景本就不建议使用 ArrayList
。
大于 size
, 空间浪费, 且在 size
处插入 null
值, 存在 NPE 隐患。
源码阅读
此部分需读者耐心阅读。
System.arraycopy()
和 Arrays.copyOf()
方法
为了便于后面内容的理解,这里先介绍 System.arraycopy()
和 Arrays.copyOf()
方法。
1 2 3 4 5 6 7 8 9 10 11 public static native void arraycopy (Object src, int srcPos, Object dest, int destPos, int length) ;
使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void main (String[] args) { int [] a = new int []{0 ,1 ,2 ,3 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; System.out.println("执行前:" ); for (int i = 0 ; i < a.length; i++) { System.out.print(a[i] + " " ); } System.arraycopy(a, 2 , a, 7 , 3 ); System.out.println("\n执行后:" ); for (int i = 0 ; i < a.length; i++) { System.out.print(a[i] + " " ); } }
Arrays.copyOf()
返回一个全新数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public static int [] copyOf(int [] original, int newLength) { int [] copy = new int [newLength]; System.arraycopy(original, 0 , copy, 0 , Math.min(original.length, newLength)); return copy; } public static <T> T[] copyOf(T[] original, int newLength) { return (T[]) copyOf(original, newLength, original.getClass()); } public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T []> newType) { @SuppressWarnings("unchecked") T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object [newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, 0 , copy, 0 , Math.min(original.length, newLength)); return copy; }
ArrayList
类中的 toArray
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public Object[] toArray() { return Arrays.copyOf(elementData, size); } public <T> T[] toArray(T[] a) { if (a.length < size) return (T[]) Arrays.copyOf(elementData, size, a.getClass()); System.arraycopy(elementData, 0 , a, 0 , size); if (a.length > size) a[size] = null ; return a; }
【拓展】使用原有 Collection
类构造新的 ArrayList
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private static final Object[] EMPTY_ELEMENTDATA = {};public ArrayList (Collection<? extends E> c) { Object[] a = c.toArray(); if ((size = a.length) != 0 ) { if (c.getClass() == ArrayList.class) { elementData = a; } else { elementData = Arrays.copyOf(a, size, Object[].class); } } else { elementData = EMPTY_ELEMENTDATA; } }
小结:
如果传入的集合类 c
大小为空,则元素列表 elementData
设为空集合。
如果传入的集合类 c
大小不为空,则将 c
中的数组处理好。
本文参考
推荐阅读:一个 Bug JDK 居然改了十年?