ArrayList的源码查看

ArrayList的源码

变量 含义 默认值
DEFAULT_CAPACITY 默认的List初始化容量 10
EMPTY_ELEMENTDATA 空实例的空数组内容 {}
DEFAULTCAPACITY_EMPTY_ELEMENTDATA 共享的空数组实例,用于默认大小的空实例。 {}
elementData 存储ArrayList内容的数组缓冲区,默认时 elementData == DEL []
size elementData数组大小

其中 elementData数组是ArrayList的存储结构,也就是说ArrayList使用数组存储元素, 但同时是使用transient进行修饰的,也就是不能序列化的,但实际上ArrayList实现了Serializable接口,那么这么做的原因时什么?

因为ArrayList具有自动扩容机制,元素到一定的大小就会自动扩容,这就会导致ArrayList中的元素一定不是满的,也就是说如果当前ArrayList的大小时50,实际元素可能远远小于50,此时就没有将全部elementData都序列化的必要了.

所以,ArrayList提供了一个writeObject方法,专门对elementData进行序列化.里面使用对小于size的所有元素进行序列化,就避免了上面阐述的问题.

1
2
3
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}

构造函数源码

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
/* 没有参数的情况下 */
public ArrayList() {
// 设置默认的内容为{}
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/* 默认是拥有默认容量(10)的空的 array list */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/* 这个默认的容量是定义好的10 */
private static final int DEFAULT_CAPACITY = 10;

/* 当参数是容量的大小的时候 */
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
/* 如果自定义初始容量大小*/
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
/* 如果参数小于0 报参数异常的错误(IllegalArgumentException) */
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/* 当参数是一个Collection结构的时候 */
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)
/* 如果不是object类型的话 转换成object类型 */
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}

常用方法源码

在看源码之前先通过下面这张图片了解以下如果新建一个ArrayList后添加一个元素的大致过程:

过程图

  • add 添加方法
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
/* 只有一个参数的添加 */
public boolean add(E e) {
/* 扩充容量 */
ensureCapacityInternal(size + 1); // Increments modCount!!
/* ArrayList其实就是个数组 将新add的值放到数组的最后面 */
elementData[size++] = e;
return true;
}

private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
/* 如果是空的话返回 默认或当前 */
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 如果elementData == {}时,返回给出的值和默认的DEFAULT_CAPACITY其中大的那个
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}

private void ensureExplicitCapacity(int minCapacity) {
/* The number of times this list has been <i>structurally modified</i>. */
// 这个modCount 指的是数据结构改变的次数(比如添加、移除元素)
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
/* 如果当前的容量大于给定的长度的时候 需要扩充 */
grow(minCapacity);
}

private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; // 原长度
int newCapacity = oldCapacity + (oldCapacity >> 1); //新长度 右移运算。长度为1.5倍的原长度
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// 使用Arrays.copyof方法进行最终扩容,所以ArrayList添加元素时比较耗费性能。
elementData = Arrays.copyOf(elementData, newCapacity);
}
  • remove 移除元素方法
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
public E remove(int index) {
//如果超出 则抛出IndexOutOfBoundsException异常
rangeCheck(index);

modCount++;
E oldValue = elementData(index);
/* 想要移除元素后面元素的个数*/
int numMoved = size - index - 1;
if (numMoved > 0)
/* arraycopy是个native方法 相当于把index+1位置上的元素挪到index位置上 */
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; //清空元素,利于GC

return oldValue;
}

/* 只会移除第一个符合条件的条目 */
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
/* 这个fastRemove方法和上面的方法大相径庭 */
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
  • contain 是否包含
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public boolean contains(Object o) {
return indexOf(o) >= 0;
}

public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
/* 内部就是for循环一个一个判断的 */
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
  • get 方法
1
2
3
4
5
6
7
8
9
public E get(int index) {
rangeCheck(index);

return elementData(index);
}
/* 本质就是返回数组位置的元素 */
E elementData(int index) {
return (E) elementData[index];
}

总结

  1. 有序性,可重复性 —> 因为内部使用数组实现,所以元素能够保证有序性且能够添加重复的元素
  2. 访问快 —> ArrayList实现了 RandomAccess 接口,能够通过get实现快速的随机访问.因为ArrayList其中内部存储是使用数组实现的,元素是连续的,通过下标就能够得到对应元素,所以检索会比较快.
  3. 插入删除慢 —> 插入和移除元素时,在扩容时,使用的是copyOf方法,也就是将原数组元素拷贝到目标数组的方式,效率低下.
  4. 非线程安全 —> ArrayList是非线程安全的,Vector是ArrayList的线程安全版本.在多线程时,可使用Vector代替ArrayList.

RandomAccess 只是一个标记接口,其中并没有实现方法,他的作用只是用来标记该结构支持随机访问.也就是说实现了这个接口的集合是支持 快速随机访问 策略的.举例来讲就是 ArrayList 的访问是随机访问,而 LinkedList的访问则是顺序访问的.
随机访问在使用fori循环时要比使用迭代器访问元素时效率高.

-------------本文结束感谢您的阅读-------------

本文标题:ArrayList的源码查看

文章作者:NanYin

发布时间:2018年10月04日 - 15:10

最后更新:2020年03月19日 - 10:03

原始链接:https://nanyiniu.github.io/2018/10/04/2018-10-04-ArrayList%E7%9A%84%E6%BA%90%E7%A0%81%E6%9F%A5%E7%9C%8B/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。