在进行ArrayList学习过程中,看到了很多人说ArrayList在无参构造时会创建一个默认容量为10的数组,但是当我查看了ArrayList的源码,却发现其无参构造时,明明指向的是一……
在进行ArrayList学习过程中,看到了很多人说ArrayList在无参构造时会创建一个默认容量为10的数组,但是当我查看了ArrayList的源码,却发现其无参构造时,明明指向的是一个空数组:
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
1
2
3
4
5
6
7
但是,仔细看一下这个无参构造函数的注释,确实是说构造一个初始容量为10的空列表,似乎有点自行矛盾呢,别急,我们再看一下DEFAULTCAPACITY_EMPTY_ELEMENTDATA的注释,你会发现它的注释存在这样的一句话,++***We distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when first element is added.***++ 这里的核心信息是当第一次进行add操作添加元素时,可以知道容量的扩容值;
再看一下elementData的源码注释,也有这样一句话: ++***Any empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA will be expanded to DEFAULT_CAPACITY when the first element is added.***++ 意思是: 添加第一个元素时,任何带有elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的空ArrayList都将扩展为DEFAULT_CAPACITY(10);
初步猜测,所有的new ArrayList<>()进行无参构造之后,指向的都是堆内存中的统一的一个地址,也就是静态存储区中DEFAULTCAPACITY_EMPTY_ELEMENTDATA所指向的那个空数组的位置,只有在进行第一次add操作之后,才会构造一个容量为10的数组。
我接着查看了ArrayList的add()方法源码,对其扩容机制进行了解,详情如下:
链接:Array的扩容操作,确实可以看到,无参构造之后,elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA,当第一次调用add()方法的时候,那么会将其扩容到容量为10;
如果看不懂,可以记得结果就行,就是确实是这么回事;同时,下面我也给出三个测试方法,也可以进行验证:
@Test
public void arrayListTest() throws Exception{
ArrayList<Integer> arrayList = new ArrayList<>();
Field elementData = ArrayList.class.getDeclaredField("elementData");
elementData.setAccessible(true);
Object[] obj = (Object[])elementData.get(arrayList);
System.out.println(obj);
ArrayList<Integer> arrayList1 = new ArrayList<>();
Field elementData1 = ArrayList.class.getDeclaredField("elementData");
elementData1.setAccessible(true);
Object[] obj1 = (Object[])elementData1.get(arrayList1);
System.out.println(obj1);
arrayList1.add(1);
Field elementData11 = ArrayList.class.getDeclaredField("elementData");
elementData11.setAccessible(true);
Object[] objects = (Object[])elementData11.get(arrayList1);
System.out.println(objects);
System.out.println(objects[0]);
System.out.println(objects[9]);
System.out.println(objects[10]);
}
@Test
public void arrayListTest1() throws Exception{
ArrayList<Integer> aa = new ArrayList<>();
Field elementData11 = ArrayList.class.getDeclaredField("DEFAULTCAPACITY_EMPTY_ELEMENTDATA");
elementData11.setAccessible(true);
Object[] obj = (Object[])elementData11.get(aa);
System.out.println(obj);
ArrayList<Integer> ba = new ArrayList<>();
Field elementData12 = ArrayList.class.getDeclaredField("DEFAULTCAPACITY_EMPTY_ELEMENTDATA");
elementData12.setAccessible(true);
Object[] obj1 = (Object[])elementData12.get(ba);
System.out.println(obj1);
System.out.println(obj1[0]);
}
@Test
public void arrayListTest2() throws Exception{
ArrayList<Integer> arrayList = new ArrayList<>();
Field elementData = ArrayList.class.getDeclaredField("elementData");
elementData.setAccessible(true);
Object[] obj = (Object[])elementData.get(arrayList);
System.out.println(obj);
System.out.println(obj[0]);
}
分别运行,arrayListTest输出:
[Ljava.lang.Object;@e6ea0c6
[Ljava.lang.Object;@e6ea0c6
[Ljava.lang.Object;@6a38e57f
1
null
java.lang.ArrayIndexOutOfBoundsException: 10
arrayListTest1输出:
[Ljava.lang.Object;@e6ea0c6
[Ljava.lang.Object;@e6ea0c6
java.lang.ArrayIndexOutOfBoundsException: 0
arrayListTest2输出:
[Ljava.lang.Object;@e6ea0c6
java.lang.ArrayIndexOutOfBoundsException: 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
这利用反射机制(正常别这样玩,这个很暴力),拿到了elementData,第一个例子里面可以看到,两次都是无参构造,然后分别取拿到elementData,输出,可以看到的是,两者的地址一致,再多几次也一样,并且,下面的第二个例子里面,也是利用反射机制拿到了DEFAULTCAPACITY_EMPTY_ELEMENTDATA,并且输出,第三个例子拿到elementData,可以发现这几个所指向的地址是一致的,也就是都是指向DEFAULTCAPACITY_EMPTY_ELEMENTDATA空数组的位置;同时,第三个例子里面输出obj[0](obj就是elementData),会报ArrayIndexOutOfBoundsException,所以再次验证了,刚刚无参构造的ArrayList就是一个空数组,并且在第一个例子中,当第一次调用add()方法时,我只调用了一次,然后输出objects[9]和objects[10],可以看到objects[9]不会报错,而objects[10],会报ArrayIndexOutOfBoundsException,也验证了第一次add操作之后,会构造出一个容量为10的数组,并且,数组的位置也会发生改变,因为在扩容过程中,进行了浅拷贝,elementData会重新指向那个新构造出来的数组。扩容具体实现可以看这篇,也是我写的:ArrayList底层原理及源码分析
最后再附上debug的截图,这里可以很清楚看到结果,和我上面用打印得到的结果一样,而且可以看到都是指向同一地方。
————————————————
原文链接:https://blog.csdn.net/weixin_43390562/java/article/details/101236959
还没有评论呢,快来抢沙发~