众所周知,ArrayList 集合的底层是通过数组来实现的,但是集合与数组还是有一些区别的,简单来说,集合的长度是可变的,而数组的长度是不可变的,通过这点可以引发出一点效率上的差异,当所处理的数据长度已知不变时,推荐用数组对其进行处理,当长度不可预知时,也只好通过集合对其进行处理。
集合转换成数组通常使用toArray()方法,这个方法还有一个重载方法为toArray(T[] a),本文主要根据以下两部分简单谈谈这两个方法的使用及区别。
第一部分为源码简析,第二部分为Demo展示。
第一部分:简单说明两个方法的源码
第一个:toArray()
下面是ArrayList类toArray()方法的源码:
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
可以看出在toArray()方法中是通过Arrays工具类对其进行copyOf()方法的调用进行数据的拷贝,并返回一个Object类型的数组
copyOf方法的源码:
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
第二个:toArray(T[] a)
下面是重载的第二个方法的源码:
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());
<strong><span style="color:#ff0000;"> System.arraycopy(elementData, 0, a, 0, size);</span></strong>
if (a.length > size)
a[size] = null;
return a;
}
可以看出,这个重载方法可以接收一个类型的数组,然后分为两种情况进行操作:
1.如果传入的数组长度不小于集合的长度(size),则集合里的数据(elementData),集合的实际长度(size),及集合的泛型(a.getClass())传入到Arrays的copyOf方法,通过方法创建一个长度与集合长度一样的新数组,并将集合里的数据拷贝到新数组中,并转型成集合的泛型的类型,将数组返回。下面是copyOf方法的源码:
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
<strong><span style="color:#ff0000;"> System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));</span></strong>
return copy;
}
可以看出,这个重载方法可以接收一个类型的数组,然后分为两种情况进行操作,如果传入的数组长度不小于集合的长度(size),则集合里的数据(elementData),集合的实际长度(size),及集合的泛型(a.getClass())传入到Arrays的copyOf方法,通过方法创建一个长度与集合长度一样的新数组,并将集合里的数据拷贝到新数组中,并转型成集合的泛型的类型,将数组返回。下面是copyOf方法的源码:
2.如果传入的数组长度大于集合的长度(size),将集合里的元素直接进行拷贝,多余的元素赋值为null。
通过红色标注的源码可知,两种情况的拷贝方式是一样的,只是有一种情况会产生新的数组实例
第二部分:简单Demo展示
注:由于第一个方法toArray()很简单,就不做展示,主要展示第二个方法toArray(T[] a)传入的数组长度是否大于集合长度的情况。
第一个例子:传入的数组长度不小于集合的长度
public static void main(String[] args) {
List<User> userList = new ArrayList<User>();
User u1 = new User(1, "ted", "nan", 24);
userList.add(u1);
User u2 = new User(2, "mead", "nan", 27);
userList.add(u2);
User u3 = new User(3, "tead", "nan", 21);
userList.add(u3);
User u4 = new User(4, "yead", "nan", 15);
userList.add(u4);
User[] u = new User[5];
User[] us = userList.toArray(u);
for (int i = 0; i < us.length; i++) {
System.out.println(us[i]);
}
<span style="white-space:pre"> </span>System.out.println(us == u);
}
例子很简单,创建一个泛型为User的集合,集合里存入四个User对象,并创建一个长度为5的数组,传入方法进行调用,然后输出数组中的数据,最后通过"=="的方式判断两个数组是否一致。下面是程序的输出结果:
User [id=1, name=ted, gender=nan, age=24]
User [id=2, name=mead, gender=nan, age=27]
User [id=3, name=tead, gender=nan, age=21]
User [id=4, name=yead, gender=nan, age=15]
null
<strong><span style="color:#ff0000;">true</span></strong>
可以看出4个对象存入数组中,传入的数组长度大于集合的长度,所以第五个值为null,并且没有创建新的数组对象,所以两个数组对象判断结果为true
第二个例子:传入的数组长度小于集合的长度
<span style="white-space: pre;"> </span>public static void main(String[] args) {
List<User> userList = new ArrayList<User>();
User u1 = new User(1, "ted", "nan", 24);
userList.add(u1);
User u2 = new User(2, "mead", "nan", 27);
userList.add(u2);
User u3 = new User(3, "tead", "nan", 21);
userList.add(u3);
User u4 = new User(4, "yead", "nan", 15);
userList.add(u4);
User[] u = new User[3];
User[] us = userList.toArray(u);
for (int i = 0; i < us.length; i++) {
System.out.println(us[i]);
}
System.out.println(us == u);
}
第二个例子与上一个类似,只是将数组的长度变为3,小于集合的长度,下面是程序的输出结果:
User [id=1, name=ted, gender=nan, age=24]
User [id=2, name=mead, gender=nan, age=27]
User [id=3, name=tead, gender=nan, age=21]
User [id=4, name=yead, gender=nan, age=15]
<strong><span style="color:#ff0000;">false</span></strong>
可以看出,四个User对象也都转成了数组的形式输出出来,并且最后的判断结果为false,表示第二个数组us指向了一个新的数组实例,而不是传入的数组u。
两个方法简单总结:
1.toArray()方法无需传入参数,可以直接将集合转换成Object数组进行返回,而且也只能返回Object类型
2.toArray(T[] a)方法需要传入一个数组作为参数,并通过泛型T的方式定义参数,所返回的数组类型就是调用集合的泛型,所以自己无需再转型,但是根据传入数组的长度 与集合的实际长度的关系,会有不同的处理;
a:数组长度不小于集合长度,直接拷贝,不会产生新的数组对象;
b:数组长度小于集合长度,会创建一个与集合长度相同的新数组,将集合的数据拷贝到新数组并将新数组的引用返回。