ArrayList源码之6260652 bug号
List的toArray()方法有多个实现,主要关注两个。原因是分析ArrayList源码时,解决构造方法中的“c.toArray might (incorrectly) not return Object[] (see 6260652)”注释。
ArrayList的构造方法源码
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;
}
}
源码中为何有“if (elementData.getClass() != Object[].class)”判断呢?
因为elementData = c.toArray();有两种类型的返回,一个是Object[] 类型,一个是具体的数组类型(如Integer[])。
- 如果c.toArray()返回的类型是Object[]时,则不需要转换,直接使用(因为源码中transient Object[]
elementData就是存List数据的数组,类型符合要求); - 如果c.toArray()返回的类型是Intger[]时,则需要转化为Object[]类型;
验证c.toArray()方法会返回两种类型的数组这一现象
测试代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TestMain {
public static void main(String[] args) {
testBug6260652();
// testBug6260652GZ();
// testBug6260652GZ2();
}
/**
* 测试toArray方法返回类型
* c.toArray might (incorrectly) not return Object[] (see 6260652)
*/
private static void testBug6260652(){
Integer[] array={1,2};
//通过Arrays.asList转换,保留了原来的基本类型
List list= Arrays.asList(array);
//转换后的数组
Object[] array3=list.toArray();
System.out.println("转换后的数组:"+array3.getClass());
System.out.println(array3.getClass()==Object[].class);
List<Integer> newList=new ArrayList<Integer>();
newList.add(4);
System.out.println("new ArrayList 转换后数组类型:"+newList.toArray());
System.out.println(newList.toArray().getClass()==Object[].class);
}
}
测试方法打印结果
转换后的数组:class [Ljava.lang.Integer;
false
new ArrayList 转换后数组类型:[Ljava.lang.Object;@71bc1ae4
true
总结现象
通过下面的代码,toArray方法返回的是Integer[]
Integer[] array={1,2};
//通过Arrays.asList转换,保留了原来的基本类型
List list= Arrays.asList(array);
//转换后的数组
Object[] array3=list.toArray();
通过下面的代码,toArray方法返回的是Object[]
在这里插入代码片List<Integer> newList=new ArrayList<Integer>();
newList.add(4);
System.out.println("new ArrayList 转换后数组类型:"+newList.toArray());
System.out.println(newList.toArray().getClass()==Object[].class);
综上所述,toArray方法确实会返回两种类型的数组,也就理解了ArrayList源码中为何有“c.toArray might (incorrectly) not return Object[] (see 6260652)”这段注释以及下面的判断逻辑。
结合ArrayList源码分析
总结上面的测试方法,已经可以确定toArray方法返回两种类型的数组。那么结合ArrayList源码继续分析,构造方法中“c.toArray might (incorrectly) not return Object[] (see 6260652)”这段注释的原因。
测试代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TestMain {
public static void main(String[] args) {
// testBug6260652();
testBug6260652GZ();
testBug6260652GZ2();
}
/**
* toArray方法返回Integer[]数组,测试构造方法
*/
private static void testBug6260652GZ(){
Integer[] array={1,2};
//通过Arrays.asList转换,保留了原来的基本类型
List list= Arrays.asList(array);
List list2 =new ArrayList<>(list);
System.out.println(list);
System.out.println(list2);
}
/**
* toArray方法返回Object[]数组,测试构造方法
*/
private static void testBug6260652GZ2(){
List<Integer> newList=new ArrayList<Integer>();
newList.add(4);
List list2 =new ArrayList<>(newList);
System.out.println(newList);
System.out.println(list2);
}
}
调试main方法进行分析
testBug6260652GZ方法中进入ArrayList构造方法时,elementData的类型是Integer[],if (elementData.getClass() != Object[].class)这个判断为true。
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();//返回的是Integer[]
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;
}
}
testBug6260652GZ2方法中进入ArrayList构造方法时,elementData的类型是Object[],if (elementData.getClass() != Object[].class)这个判断为false。
在这里插入代码片public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();//返回的是Object[]
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;
}
}
综上所述,构造方法中“c.toArray might (incorrectly) not return Object[] (see 6260652)”这段注释的原因是elementData = c.toArray()返回的类型可能是Object[],也可能是具体的数组类型,如Integer[]。
为何toArray会返回两种数组类型呢?
依旧用上述测试代码进行调试,跟踪 elementData = c.toArray()中toArray方法的实现。
当从testBug6260652GZ方法入口进入构造时,断点如下图:
当从testBug6260652GZ2方法入口进入构造时,断点如下图:
结合上面两张图可以看到,两个方法分别进入两个实现:
- Object[] Arrays$ArrayList.toArray() :Arrays的内部类ArrayList的方法
- Object[] ArrayList.toArray():java.util.ArrayList的方法
为何会出现两个实现呢?
因为List list= Arrays.asList(array);返回的是Arrays.ArrayList,而 List newList=new ArrayList();返回的是java.util.ArrayList