在JDK内置的集合框架中,Collection接口提供了集合转数组的的两个方法
Object[] toArray();
<T> T[] toArray(T[] a);
对于无参的toArray()函数,其返回值类型为Object[] ,此时不能强制转换成其他类型,若强转将会抛出ClassCastException异常
代码测试
package basicKnowledge.集合框架.treeset;
import java.util.TreeSet;
/**
* @基本功能:
* @program:summary
* @author:peicc
* @create:2019-08-01 11:10:16
**/
public class TestTreeSet {
public static void main(String[] args) {
testTreeSetAPIS();
}
public static void testTreeSetAPIS(){
TreeSet treeSet=new TreeSet();
//测试集合的有序性
treeSet.add("aaa");
treeSet.add("bbb");
treeSet.add("eee");
treeSet.add("ddd");
String array1[]=(String[])treeSet.toArray();
for (Object object:array1) {
System.out.printf("for each:%s\n",object);
}
}
}
运行结果
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
at basicKnowledge.集合框架.treeset.TestTreeSet.testTreeSetAPIS(TestTreeSet.java:45)
at basicKnowledge.集合框架.treeset.TestTreeSet.main(TestTreeSet.java:13)
因此,对于集合转数组,我们通常采用第二种有参的函数,但此时我们需要关注下传递的参数:
- 第一种情况:传递的数组大小等于集合的大小
- 第二种情况:传递的数组大小大于集合的大小
- 第三种情况:传递的数组大小小于集合的大小
代码测试
public static void testToArray(){
TreeSet treeSet=new TreeSet();
//测试集合的有序性
treeSet.add("chang");
treeSet.add("cheng");
treeSet.add("ge");
//数组大小等于集合大小
System.out.println("-------------数组大小等于集合大小------------------");
String array1[]=(String[])treeSet.toArray(new String[treeSet.size()]);
System.out.println(array1.length);
for(String str:array1){
System.out.printf("for each:%s\n",str);
}
//数组大小小于集合大小
System.out.println("-------------数组大小小于集合大小------------------");
String array2[]=(String[])treeSet.toArray(new String[0]);
System.out.println(array2.length);
for(String str:array2){
System.out.printf("for each:%s\n",str);
}
//数组大小大于集合大小
System.out.println("-------------数组大小大于集合大小------------------");
String array3[]=(String[])treeSet.toArray(new String[10]);
System.out.println(array3.length);
for(String str:array3){
System.out.printf("for each:%s\n",str);
}
}
运行结果
-------------数组大小等于集合大小------------------
3
for each:chang
for each:cheng
for each:ge
-------------数组大小小于集合大小------------------
3
for each:chang
for each:cheng
for each:ge
-------------数组大小大于集合大小------------------
10
for each:chang
for each:cheng
for each:ge
for each:null
for each:null
for each:null
for each:null
for each:null
for each:null
for each:null
Process finished with exit code 0
对于第一种、第三种情况我们或许能理解,但对于第二种情况,当我们传进大小为0的数组时,为什么也能将集合正常转换成数组?我初步猜测是因为当我们将数组引用传递进去的时候,该方法会对数组的长度与集合的长度进行判断,然后再进行转换。于是,进一步我变通过源码分析验证了我的猜想。
public <T> T[] toArray(T[] a) {
// Estimate size of array; be prepared to see more or fewer elements
int size = size();
T[] r = a.length >= size ? a :
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) { // fewer elements than expected
if (a == r) {
r[i] = null; // null-terminate
} else if (a.length < i) {
return Arrays.copyOf(r, i);
} else {
System.arraycopy(r, 0, a, 0, i);
if (a.length > i) {
a[i] = null;
}
}
return a;
}
r[i] = (T)it.next();
}
// more elements than expected
return it.hasNext() ? finishToArray(r, it) : r;
}
通过源码,我们可以看见,当数组的长度小于集合大小时,通过反射机制重新构造一个与集合大小相同的数组。当数组的长度大于集合大小时,返回的数组长度为传进去的数组长度,多余元素以null填充
结论
- 集合转数组时,请使用带参数的toArray()方法,并且传进去的参数类型与大小请与集合的元素类型与大小一致
- 虽然当传递进去的数组大小小于集合大小时,也能得到正确结果,但经过反射生成数组需要花费一些时间,对性能有些许影响
long start=System.currentTimeMillis();
for (int i = 0; i <100000 ; i++) {
String array4[]=(String[])treeSet.toArray(new String[0]);
}
long end=System.currentTimeMillis();
System.out.println("用时:"+(end-start));
start=System.currentTimeMillis();
for (int i = 0; i <100000; i++) {
String array5[]=(String[])treeSet.toArray(new String[treeSet.size()]);
}
end=System.currentTimeMillis();
System.out.println("用时:"+(end-start));
时间相差两至三倍