这次简单看下ArrayList的实现过程,以及它拥有的操作方法。
在Java 8 中 ArrayList 的实现 较以前有很大的改变。
ArrayList 拥有的属性
1 | public class ArrayList<E> extends AbstractList<E> |
- 实现的接口看出,支持随机访问,克隆,序列化;
- 默认大小
DEFAULT_CAPACITY
为 10 ; elementData
存储数组数据的,是 Object[] 类型的数组;size
为当前 ArrayList 的实际大小。构造函数
ArrayList 通过构造方法创建有三种方法: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/**
* 构造一个指定初始容量的空列表
* @param initialCapacity ArrayList的初始容量
* @throws IllegalArgumentException 如果给定的初始容量为负值
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
// 构造一个默认初始容量为10的空列表,但是还没分配大小。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 构造一个包含指定collection的元素的列表,这些元素按照该collection的迭代器返回的顺序排列的
* @param c 包含用于去构造ArrayList的元素的collection
* @throws NullPointerException 如果指定的collection为空
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray()可能不会正确地返回一个 Object[]数组,那么使用Arrays.copyOf()方法
if (elementData.getClass() != Object[].class)
//Arrays.copyOf()返回一个 Object[].class类型的,大小为size,元素为elementData[0,...,size-1]
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
添加元素add
最简单的添加方法,在 ArrayList 尾部添加一个元素,需要去扩容,这个是ArrayList 最重要的一个特点;
1 | public boolean add(E e) { |
扩容
下面是扩容的重要代码:
1 | protected transient int modCount = 0; |
每当向数组中添加元素时,都要去检查添加元素后的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,都回去调用方法ensureCapacityInternal(int minCapacity)
在这个方法中看到,那个if语句判断就是,我们使用默认无参的构造函数创建的ArrayList 是在这里去 给大小的,如果第一次 add 的元素长度大于默认长度的话,就是用新的长度,否则给默认大小10;
给定大小后,就去调用grow
方法,进行扩容。
看到int newCapacity = oldCapacity + (oldCapacity >> 1);
ArrayList 每次扩容的大小是当前容量的0.5倍,就是默认大小为10,下次扩容后大小为15,下次再扩容后为 15 1.5*;所以ArrayList每次扩容的容量只会越来越大。
modCount
用于记录ArrayList的结构性变化的次数,add()、remove()、addall()、removerange()及clear()方法都会让modCount增长。
其余的add方法,addAll
1 | //将指定的元素(E e)添加到此列表的尾部 |
remove 删除元素
1 | /** |
修改元素 set
1 | //将指定索引上的值替换为新值,并返回旧值 |
查询
1 | //判断ArrayList中是否包含Object(o) |
其他方法
1 | //将底层数组的容量调整为当前列表保存的实际元素的大小的功能 |
遍历方法
1 | package com.wuwii.test; |
Iterator与ListIterator的区别:
- Iterator可以应用于所有的集合,Set、List和Map和这些集合的子类型。而ListIterator只能用于List及其子类型;
- Iterator只能实现顺序向后遍历,ListIterator可实现顺序向后遍历和逆向(顺序向前)遍历;
- Iterator只能实现remove操作,ListIterator可以实现remove操作,add操作,set操作。
多线程中使用ArrayList
当多个线程同时修改一个ArrayList对象的时候,必须要保持外部同步操作,但是ArrayList不是同步的,非线程安全,有一种办法就是可以使用Collections.synchronizedList
进行包装:1
List list = Collections.synchronizedList(new ArrayList(...));
但是在平时开发中,多线程开发中多选择使用Vector
或者CopyOnWriteArrayList
。
补充:toArray 方法
toArray()
返回的是java.lang.Object的数组,不建议使用类型强转,可能会存在空指针异常。
toArray(T[])
源码:
1 | public <T> T[] toArray(T[] var1) { |
所以上面就会出现一个现象,索引为 size 的元素,一直会为null ,可以检验下的。