一、开门见山
两种可以直接把要添加元素写在参数列表里的方法:
第一种:Arrays.asList 方式
List<Integer> l1 = new ArrayList<>(Arrays.asList(1, 2, 3));
第二种:Collections.addAll 方式
List<Integer> l2 = new ArrayList<>();
Collections.addAll(l2, 1, 2, 3);
二、抽丝剥茧
ArrayList 自身的构造函数和两个 addAll 方法都不支持可变参数,
public boolean addAll(Collection<? extends E> c)
public boolean addAll(int index, Collection<? extends E> c)
2.1 Arrays.asList 方式
Arrays.asList 实现:
// asList 方法
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
// Arrays 内部静态类 ArrayList,和 java.util 包下的 ArrayList 同名,两者没有关系
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
@Override
public int size() {
return a.length;
}
@Override
public Object[] toArray() { // toArray 方法借助 Object 的 clone 方法实现
return a.clone();
}
// ...
}
这里的 ArrayList 是 Arrays 的静态内部类,最大的特点是不能增删元素。
以 asList 可变参数数组为底层存储结构。
其中 toArray 方法通过 Object 的 clone 方法实现:
protected native Object clone() throws CloneNotSupportedException;
ArrayList 参数为 Collection 的构造函数:
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class); // 会执行到这儿
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
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;
}
Arrays.copyOf()方法详解-jdk1.8
大致要点:
newType 的类型是 Class<? extends T[]>,T 是一个泛型,如果与 Object 做比较的话,父子关系肯定是 Object >= T 的,由于 == 只能在同类型中才能做比较,所以需要强制转换。
class 对象是单例模式,所以可以用 == 比较。
方法目的:数组向上转型并拷贝
总结前面的代码,待添加元素经过两次拷贝后,添加成功。
- 第一次拷贝发生在,ArrayList 构造函数的第一句
elementData = c.toArray();
,通过 Object 的 clone 方法进行了一次拷贝。 - 第二次拷贝还是发生在 ArrayList 构造函数,
elementData = Arrays.copyOf(elementData, size, Object[].class);
解释一下为什么 elementData.getClass() != Object[].class 为 true:
因为执行完 Arrays.asList(1, 2, 3) 后,底层的数组类型为 Integer[]。
所以下面这段代码的执行结果为 true,true。
import java.util.Arrays;
public class Test {
public static void main(String[] args){
System.out.println(Arrays.asList(1, 2, 3).toArray().getClass() == Integer[].class);
// 显式类型声明
System.out.println(Arrays.<Object>asList(1, 2, 3).toArray().getClass() == Object[].class);
}
}
2.2 Collections.addAll 方式
public static <T> boolean addAll(Collection<? super T> c, T... elements) {
boolean result = false;
for (T element : elements)
result |= c.add(element);
return result;
}
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
三、结论
初始元素少的话,直接写在可变参数里,喜欢用哪种就用哪种,几个元素的添加不会有什么效率问题。
初始元素多的话,比如添加一个大数组,推荐使用 Arrays.asList 方式,本地方法拷贝会快得多,尽管拷贝两次。
网友评论