Java集合源码分析(二)ArrayList

ArrayList简介

  ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存。

  ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。

  ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问,实现了Cloneable接口,能被克隆。

ArrayList源码

  ArrayList的源码如下(加入了简单的注释,版本号为1.56):

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
/**@(#)ArrayList.java 1.56 06/04/21
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
 
package java.util;
 
/**
 * @author  Josh Bloch
 * @author  Neal Gafter
 * @version 1.56, 04/21/06
 * @see     Collection
 * @see     List
 * @see     LinkedList
 * @see     Vector
 * @since   1.2
 */
 
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;
 
    // ArrayList基于该数组实现,用该数组保存数据
    private transient Object[] elementData;
 
    // 实际大小
    private int size;
 
    // 带容量大小的构造函数
    public ArrayList(int initialCapacity) {
    super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
    this.elementData = new Object[initialCapacity];
    }
 
    // 默认构造函数
    public ArrayList() {
    this(10);
    }
 
    // Collection构造函数
    public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    size = elementData.length;
    // c.toArray might (incorrectly) not return Object[] (see 6260652)
    if (elementData.getClass() != Object[].class)
        elementData = Arrays.copyOf(elementData, size, Object[].class);
    }
 
    // 当前容量值为实际个数
    public void trimToSize() {
    modCount++;
    int oldCapacity = elementData.length;
    if (size < oldCapacity) {
            elementData = Arrays.copyOf(elementData, size);
    }
    }
   // 确定ArrayList容量
    // 若容量不足以容纳当前全部元素,则扩容,新的容量=“(原始容量x3)/2 + 1”
    public void ensureCapacity(int minCapacity) {
    modCount++;
    int oldCapacity = elementData.length;
    if (minCapacity > oldCapacity) {
        Object oldData[] = elementData;
        int newCapacity = (oldCapacity * 3)/2 + 1;
            if (newCapacity < minCapacity)
        newCapacity = minCapacity;
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
    }
    }
 
    // 返回实际大小
    public int size() {
    return size;
    }
 
    // 清空
    public boolean isEmpty() {
    return size == 0;
    }
 
    // 是否包含o
    public boolean contains(Object o) {
    return indexOf(o) >= 0;
    }
 
    // 正向查找,返回o的index
    public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
        if (elementData[i]==null)
            return i;
    } else {
        for (int i = 0; i < size; i++)
        if (o.equals(elementData[i]))
            return i;
    }
    return -1;
    }
 
    // 逆向查找,返回o的index
    public int lastIndexOf(Object o) {
    if (o == null) {
        for (int i = size-1; i >= 0; i--)
        if (elementData[i]==null)
            return i;
    } else {
        for (int i = size-1; i >= 0; i--)
        if (o.equals(elementData[i]))
            return i;
    }
    return -1;
    }
 
    // 克隆函数
    public Object clone() {
    try {
        ArrayList<E> v = (ArrayList<E>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError();
    }
    }
 
    // 返回ArrayList的Object数组
    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }
 
    // 返回ArrayList组成的数组
    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());
    System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }
 
    // Positional Access Operations
 
    // 得到index位置的元素
    public E get(int index) {
    RangeCheck(index);
 
    return (E) elementData[index];
    }
 
    // 向index插入element
    public E set(int index, E element) {
    RangeCheck(index);
 
    E oldValue = (E) elementData[index];
    elementData[index] = element;
    return oldValue;
    }
 
    // 添加e
    public boolean add(E e) {
    ensureCapacity(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
    }
 
    // 向index插入element
    public void add(int index, E element) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(
        "Index: "+index+", Size: "+size);
 
    ensureCapacity(size+1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
             size - index);
    elementData[index] = element;
    size++;
    }
 
    // 移除index位置的元素
    public E remove(int index) {
    RangeCheck(index);
 
    modCount++;
    E oldValue = (E) elementData[index];
 
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                 numMoved);
    elementData[--size] = null; // Let gc do its work
 
    return oldValue;
    }
 
    // 移除o
    public boolean remove(Object o) {
    if (o == null) {
            for (int index = 0; index < size; index++)
        if (elementData[index] == null) {
            fastRemove(index);
            return true;
        }
    } else {
        for (int index = 0; index < size; index++)
        if (o.equals(elementData[index])) {
            fastRemove(index);
            return true;
        }
        }
    return false;
    }
 
    // 快速移除index位置的元素
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // Let gc do its work
    }
 
    // 清空
    public void clear() {
    modCount++;
 
    // Let gc do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;
 
    size = 0;
    }
 
    // 添加Collection
    public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
        int numNew = a.length;
    ensureCapacity(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
    return numNew != 0;
    }
 
    // 在index添加Collection
    public boolean addAll(int index, Collection<? extends E> c) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(
        "Index: " + index + ", Size: " + size);
 
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacity(size + numNew);  // Increments modCount
 
    int numMoved = size - index;
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
                 numMoved);
 
        System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;
    }
 
    // 移除fromIndex到toIndex之间的全部元素
    protected void removeRange(int fromIndex, int toIndex) {
    modCount++;
    int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);
 
    // Let gc do its work
    int newSize = size - (toIndex-fromIndex);
    while (size != newSize)
        elementData[--size] = null;
    }
 
    // 移除index位置的元素
    private void RangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(
        "Index: "+index+", Size: "+size);
    }
 
    // java.io.Serializable的写入函数,将ArrayList的“容量,所有的元素值”都写入到输出流中
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();
 
        // Write out array length
        s.writeInt(elementData.length);
 
    // Write out all elements in the proper order.
    for (int i=0; i<size; i++)
            s.writeObject(elementData[i]);
 
    if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
 
    }
 
    // java.io.Serializable的读取函数:根据写入方式读出,先将ArrayList的“容量”读出,然后将“所有的元素值”读出
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
    // Read in size, and any hidden stuff
    s.defaultReadObject();
 
        // Read in array length and allocate array
        int arrayLength = s.readInt();
        Object[] a = elementData = new Object[arrayLength];
 
    // Read in all elements in the proper order.
    for (int i=0; i<size; i++)
            a[i] = s.readObject();
    }
}

 

ArrayList详细分析

1.构造函数

ArrayList有三个构造函数,如下(英文注释全部删掉,默认代码折叠,太占地方了):

  从第一句话可以看到,ArrayList本质上是一个Object类型的数组,前面加入了transient关键字,在序列化的时候忽略,但是在最后自己重写了writeObject和readObject这两个函数,第一个是构造传入固定大小的ArrayList,第二个是默认大小为10,第三个是将传入的Collection转成ArrayList。

  序列化有2种方式: 

  A、只是实现了Serializable接口。 

    序列化时,调用java.io.ObjectOutputStream的defaultWriteObject方法,将对象序列化。 

    注意:此时transient修饰的字段,不会被序列化。 

  B、实现了Serializable接口,同时提供了writeObject方法。 

    序列化时,会调用该类的writeObject方法。而不是java.io.ObjectOutputStream的defaultWriteObject方法。 

    注意:此时transient修饰的字段,是否会被序列化,取决于writeObject

2.自动扩容函数

  关键在这里int newCapacity = (oldCapacity * 3)/2 + 1;新的数组大小是旧的数组大小的二分之三加一,然后调用Arrays.copyOf(elementData, newCapacity);得到新的elementData对象。说到这里我默默的翻看了一下jdk1.7的源码,发现在jdk1.7当中,扩容效率有了本质上的提高,请看下面的代码:(出自jdk1.7)

  1.7相比较1.6,自动扩容增加了两个方法,增加了数组扩容时的判断,最重要的是这句话:int newCapacity = oldCapacity + (oldCapacity >> 1);没有再用*3再/2这种低端的玩法,直接采用了移位运算,我不是太懂十进制数的移位运算,经过几次自己的测试发现如果是偶数,这个移位运算正好是一半,如果是奇数,则是向下取整。

 

3.存储

  第一判断ensureSize,如果够直接插入,否则按照policy扩展,复制,重建数组。

  第二步插入元素。

  ArrayList提供了set(int index, E element)、add(E e)、add(int index, E element)、addAll(Collection<? extends E> c)、addAll(int index, Collection<? extends E> c)这些添加元素的方法。

  3.1. set(int index, E element),取代,而非插入,返回被取代的元素

  3.2.add(E e) 增加元素到末尾,如果size不溢出,自动增长

  3.3.add(int index, E element) 增加元素到某个位置,该索引之后的元素都后移一位

  3.4.后面两个方法都是把集合转换为数组利用c.toArray,然后利用Arrays.copyOF 方法

4.删除

一种是按索引删除,不用查询,索引之后的element顺序左移一位,并将最后一个element设为null,由gc负责回收。

 

5.Arrays.copyOf

  源码如下:

  这里有所优化,如果是Object类型的,直接new Object数组,如果不是则通过Array.newInstance(newType.getComponentType(), newLength)方法产生相应的数组类型。通过System.arraycopy实现数组复制,System是个final类,arraycopy是个native方法。

  该方法被标记了native,调用了系统的C/C++代码,在JDK中是看不到的,但在openJDK中可以看到其源码。该函数实际上最终调用了C语言的memmove()函数,因此它可以保证同一个数组内元素的正确复制和移动,比一般的复制方法的实现效率要高很多,很适合用来批量处理数组。Java强烈推荐在复制大量数组元素时用该方法,以取得更高的效率。

6.Arrays.newInstance()的意义

  Java反射技术除了可以在运行时动态地决定要创建什么类型的对象,访问哪些成员变量,方法,还可以动态地创建各种不同类型,不同维度的数组。

 

  动态创建数组的步骤如下:
    1.创建Class对象,通过forName(String)方法指定数组元素的类型
    2.调用Array.newInstance(Class, length_of_array)动态创建数组

 

  访问动态数组元素的方法和通常有所不同,它的格式如下所示,注意该方法返回的是一个Object对象
  Array.get(arrayObject, index)

 

  为动态数组元素赋值的方法也和通常的不同,它的格式如下所示, 注意最后的一个参数必须是Object类型
  Array.set(arrayObject, index, object)

 

  动态数组Array不单可以创建一维数组,还可以创建多维数组。步骤如下:
    1.定义一个整形数组:例如int[] dims= new int{5, 10, 15};指定一个三维数组
    2.调用Array.newInstance(Class, dims);创建指定维数的数组

 

  访问多维动态数组的方法和访问一维数组的方式没有什么大的不同,只不过要分多次来获取,每次取出的都是一个Object,直至最后一次,赋值也一样。

 

  动态数组Array可以转化为普通的数组,例如:
  Array arry = Array.newInstance(Integer.TYPE,5);
  int arrayCast[] = (int[])array;

7.为何要序列化

  ArrayList 实现了java.io.Serializable接口,在需要序列化的情况下,复写writeObjcet和readObject方法提供适合自己的序列化方法。

  1、序列化是干什么的?

    简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。虽然你可以用你自己的各种各样的方法来保存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。

  2、什么情况下需要序列化

    a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;

    b)当你想用套接字在网络上传送对象的时候;

    c)当你想通过RMI传输对象的时候;

8.总结

  8.1.Arraylist基于数组实现,是自增长的

  8.2.非线程安全的

  8.3.插入时可能要扩容,删除时size不会减少,如果需要,可以使用trimToSize方法,在查询时,遍历查询,为null,判断是否是null, 返回; 如果不是null,用equals判断,返回

  8.4. 允许重复和 null 元素

 ————————————————————————————————————————————————————————————

参考资料:

【Java集合源码剖析】ArrayList源码剖析

集合类学习之Arraylist 源码分析

posted @   极客挖掘机  阅读(708)  评论(1编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
点击右上角即可分享
微信分享提示