Array 和 List 都是咱们在开发过程当中常见的数据结构。咱们都知道 Array 是定长的,List 是可变长。并且,List 的实现类 ArrayList 也是根据 Array 去实现的。java
如下 Array 指代数组,List 指代数组列表。
固然最原始的方法就是使用遍历的方式,将 Array 中的元素都添加到 List 中。这种实现方式这里不做赘述。数组
Java1.2 以后,Jdk 语言提供 Arrays 这个工具类。大大简化了咱们常见的 Array 操做,可是也有很多须要注意的问题。数据结构
以下:函数
Integer[] a = { 1, 2 }; List<Integer> l = Arrays.asList(a);
这是咱们常见的 Array 转换 List 的方式,可是这个使用上有一个问题。当对 List 对象 l 进行列表插入操做时:工具
l.add(3);
程序就会抛出异常 java.lang.UnsupportedOperationException
。这是为何呢?ui
查看 Arrays.asList
源码发现,设计
public static <T> List<T> asList(T... a) { return new ArrayList<>(a); }
这里返回的 ArrayList 并非 java.util.ArrayList
而是 java.util.Arrays.ArrayList
。Arrays 又新建了一个 ArrayList 内部类,实现了一些基本 get set 方法。code
回头查看 java.util.Arrays.ArrayList.ArrayList(E[] array)
构造函数,对象
ArrayList(E[] array) { a = Objects.requireNonNull(array); }
不难发现,java.util.Arrays.ArrayList
虽然打着 List 的旗号,继承了 AbstractList
。可是其只是在 Array 的基础上进行了简单的封转,AbstractList
中则是直接重写了 add 方法,表示这个方法是不容许操做。blog
public void add(int index, E element) { throw new UnsupportedOperationException(); }
明白了这个错误产生的缘由,回头想一下 Java 的这些开发者们为何这样设计。
ArrayList 中若是要添加一个元素,则须要先对其内部的 Array 进行扩容,而后将 Old Array 复制到扩容后的 New Array 中。若是 Array 转 List 仅仅是读取操做,或是在 Array 的 Size 范围以内进行替换操做,再将 Array 复制一遍,难免会对内存进行浪费,倒不如直接将原始的 Array 直接拿来维护更为直接和高效(正如java.util.Arrays.ArrayList
的实现方式)。
明白这个原因以后,若是要在 Array 转 List 以后,不仅有只读操做,那么则须要下面的实现,
List<Integer> l = new ArrayList<>(Arrays.asList(a));
虽然在咱们平常的开发过程当中,已经习惯了使用 ArrayList 去代替 Array,可是了解此处 Java 的转换过程仍是可以让咱们少踩坑。
由于 Array 的长度不可变,因此这个转换过程当中,会有长度不匹配的状况。
常见的转换方法是 ArrayList.toArray()
或 ArrayList.toArray(T[])
。
这两个实现的共同点是都是对 ArrayList 中的 Array 进行 Copy 操做,生成一个新的数组返回。不一样点是前者返回值是 Object[]
,后者是 T[]
。
在 ArrayList.toArray(T[])
的使用过程当中须要注意,当要转换的 Array 长度小于 ArrayList 的 size 时,不要试图经过传入形参的方式进行转换,虽然这在 Array 的长度大于 List 时不会出现问题。
以下代码:
// l [1, 2, 3] Integer[] a = new Integer[2]; l.toArray(a); // error 正确写法:a = l.toArray(a); Stream.of(a).forEach(System.out::println);
输出结果是:null null
。
查看源码实现:
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; } // Arrays.copyOf 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; }
可见,当 a.length < size
成立,入参 a
并无被使用,因此 a
依然是 new Integer[2]
。
因此,极度建议在转换以前初始化 Array 的长度为 ArrayList 的 size,而且使用返回值从新给 Array 赋值。
// l [1, 2, 3] Integer[] b = new Integer[l.size()]; l.toArray(b); Stream.of(b).forEach(System.out::println);
原文地址:https://xdbin.com/blog/8a9eec5167da2d4501681e8c3f180001