码迷,mamicode.com
首页 > 其他好文 > 详细

JDK源码分析之集合02ArrayList

时间:2016-04-19 22:49:09      阅读:344      评论:0      收藏:0      [点我收藏+]

标签:

一、前言

????有了前一篇对集合类的概述,我们知道ArrayList是属于Collection类系中的一个具体实现类,其特点是长度可以动态改变,集合内部使用数组保存元素。下面我们对源码进行分析。

二、ArrayList源代码分析

????2.1 类的继承关系

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{}

?

说明:可以看出ArrayList类实现了ListRandomAccessCloneableSerializable接口。我们可以看出ArrayListList类系中的具体类。RandomAccess用于支持快速随机访问,Cloneable用于支持深拷贝。同时还继承了AbstractList抽象类,使Arraylist可以选择性的实现List接口中的方法。在定义的时候使用了泛型来支持类型动态加载。

2.2类的属性

/**

???? * The array buffer into which the elements of the ArrayList are stored. The

???? * capacity of the ArrayList is the length of this array buffer.

???? */

???? // Object数组,用于保存ArrayList的元素,此数组的长度为ArrayList的容量

???? private transient Object[] elementData;

???? /**

???? * The size of the ArrayList (the number of elements it contains).

???? */

???? // ArrayList的长度,即此ArrayList元素的个数

???? private int size;

???? //定义数组最大的size

???? private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

?

????说明:ArrayList中定义了一个Object的数组用于保存元素,故归根结底对于ArrayList的所有操作其实都是对数组的操作,因此在随机访问时效率高,而和改变数组大小相关的操作如插入删除操作效率低。

2.3构造方法

/**

???? * Constructs an empty list with the specified initial capacity.

???? */

????// 定义一个指定大小的ArrayList,如果参数小于0,抛出异常信息

????public ArrayList(int initialCapacity) {

????????super();

????????if (initialCapacity < 0)

????????????throw new IllegalArgumentException("Illegal Capacity: "

????????????????????+ initialCapacity);

????????// 定义一个指定大小的Object数组

????????this.elementData = new Object[initialCapacity];

????}

?

????/**

???? * Constructs an empty list with an initial capacity of ten.

???? */

????// 无参构造函数,此时使用默认容量大小10

????public ArrayList() {

????????// 调用上面的带参构造函数

????????this(10);

????}

?

????/**

???? * Constructs a list containing the elements of the specified collection, in

???? * the order they are returned by the collection‘s iterator.

???? */

????// 创建一个ArrayList,其中包含参数集合中的所有元素,顺序为迭代器返回的顺序

????public ArrayList(Collection<? extends E> c) {

?

????????elementData = c.toArray();

????????size = elementData.length;

????????// c.toArray might (incorrectly) not return Object[] (see 6260652)

????????// 当调用toArray函数返回的类型不为Object数组使,使用Arrays.copyOf()函数完成拷贝

????????if (elementData.getClass() != Object[].class)

????????????elementData = Arrays.copyOf(elementData, size, Object[].class);

????}

?

????说明:ArrayList支持三种构造方式:默认大小、指定大小和指定元素。其中使用指定元素或指定大小构造时,所得到的ArrayList对象可以正常使用其所有函数完成增删改等操作。

2.4核心函数分析

1contains(Object o)函数

????

/**

???? * Returns the index of the first occurrence of the specified element in

???? * this list, or -1 if this list does not contain the element.

???? */

????// 返回特定元素(包括null)第一次出现的index,如果不存在则返回-1,比较相等时用的是equals方法

????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;

????}

?

????public boolean contains(Object o) {

//indexOf返回值大于0时说明存在

????????return indexOf(o) >= 0;

????}

?

说明:使用containes或者indexOf方法时需要重写equals方法。

2、添加元素的函数

????

/**

???? * Appends the specified element to the end of this list.

???? */

????public boolean add(E e) {

????????ensureCapacityInternal(size + 1); // Increments modCount!!

????????//扩充数组后将元素添加到数组中

????????elementData[size++] = e;

????????return true;

????}

?

在添加元素时,首先要确保保存元素的数组能够再添加一个元素。在add函数内部首先调用了ensureCapacityInternal函数;函数代码如下:

private void ensureCapacityInternal(int minCapacity) {

????????// 大小被改变的次数加1

????????modCount++;

????????// overflow-conscious code

????????//如果最小需要容量大于数组的长度时,需要对数组长度扩充

????????if (minCapacity - elementData.length > 0)

????????????grow(minCapacity);

????}

?

此函数比较存放元素所需空间和数组的长度,如果长度不够,则调用grow函数扩充数组容量,参数为元素个数。函数代码如下:

????

private void grow(int minCapacity) {

????????// overflow-conscious code

????????int oldCapacity = elementData.length;

????????// 将数组原始长度又移一位加上原始长度,得到新的数组长度

????????int newCapacity = oldCapacity + (oldCapacity >> 1);

????????//如果新的数组长度仍然小于元素个数,则直接将元素个数当作新的数组长度

????????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:

????????//将数组元素复制到新的数组

????????elementData = Arrays.copyOf(elementData, newCapacity);

????}

?

grow函数按照一定的规则获取新的数组长度,并将元素复制到新的数组中,其中如果新的数组长度大于支持的最大值时,会调用hugeCapacity函数获取获取整数的最大值作为数组长度。其中函数源代码如下:

private static int hugeCapacity(int minCapacity) {

????????if (minCapacity < 0) // overflow

????????????throw new OutOfMemoryError();

????????return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE

????????????????: MAX_ARRAY_SIZE;

????}

?

添加元素大致的流程是:调用add方法----------->扩充数组-------------->添加元素

将元素添加到指定index中:

public void add(int index, E element) {

????????// 检查index是否合法,小于0或者大于数组长度都将抛出异常

????????rangeCheckForAdd(index);

????????// 扩充容量

????????ensureCapacityInternal(size + 1); // Increments modCount!!

????????//index索引后面的元素全部向后移动一位

????????System.arraycopy(elementData, index, elementData, index + 1, size

????????????????- index);

????????elementData[index] = element;

????????size++;

????}

?

添加多个元素到集合的末尾

public boolean addAll(Collection<? extends E> c) {

????????// 获取集合对应的数组

????????Object[] a = c.toArray();

????????// 获取数组长度

????????int numNew = a.length;

????????// 扩充容量

????????ensureCapacityInternal(size + numNew); // Increments modCount

????????// 将集合元素添加到数组的末尾

????????System.arraycopy(a, 0, elementData, size, numNew);

????????size += numNew;

????????return numNew != 0;

????}

?

????添加多个元素到指定index

public boolean addAll(int index, Collection<? extends E> c) {

????????// 检查index是否合法

????????rangeCheckForAdd(index);

????????Object[] a = c.toArray();

????????int numNew = a.length;

????????// 扩充数组

????????ensureCapacityInternal(size + numNew); // Increments modCount

?

????????int numMoved = size - index;

????????// 移动index以后的元素

????????if (numMoved > 0)

????????????System.arraycopy(elementData, index, elementData, index + numNew,

????????????????????numMoved);

????????//将元素添加到到数组中

????????System.arraycopy(a, 0, elementData, index, numNew);

????????size += numNew;

????????return numNew != 0;

????}

?

????

????说明:当在中间插入元素时,要对数组元素进行重排序,因此效率要低。

3、删除元素函数

删除指定index的元素

E elementData(int index) {

????????return (E) elementData[index];

????}

????public E remove(int index) {

????????// 检查index的合法性

????????rangeCheck(index);

????????modCount++;

????????// 保存要删除的元素

????????E oldValue = elementData(index);

?

????????int numMoved = size - index - 1;

????????if (numMoved > 0)

????????????System.arraycopy(elementData, index + 1, elementData, index,

????????????????????numMoved);

????????// 将最后一个元素设置为null

????????elementData[--size] = null; // Let gc do its work

?

????????return oldValue;

????}

?

删除操作实质上是用后面的元素覆盖掉前面要删除的元素,最后将末尾元素设置为null的操作

删除指定第一次出现的元素

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 boolean remove(Object o) {

????????if (o == null) {

????????????//可以remove一个null元素

????????????for (int index = 0; index < size; index++)

????????????????if (elementData[index] == null) {

????????????????????fastRemove(index);

????????????????????return true;

????????????????}

????????} else {

????????????for (int index = 0; index < size; index++)

????????????????//以来equals方法,所以元素需要重写equals方法

????????????????if (o.equals(elementData[index])) {

????????????????????fastRemove(index);

????????????????????return true;

????????????????}

????????}

????????return false;

????}

?

删除操作依赖于equals方法,并且可以删除null

删除指定集合中的所有元素,该方法依赖containes方法比较元素,因此元素同样需要重新定义equals方法

private boolean batchRemove(Collection<?> c, boolean complement) {

????????final Object[] elementData = this.elementData;

????????int r = 0, w = 0;

????????boolean modified = false;

????????try {

????????????for (; r < size; r++)

????????????????if (c.contains(elementData[r]) == complement)

????????????????????elementData[w++] = elementData[r];

????????} finally {

????????????// Preserve behavioral compatibility with AbstractCollection,

????????????// even if c.contains() throws.

????????????//如果有异常发生,将发生异常后面的元素直接拷贝到数组中

????????????if (r != size) {

????????????????System.arraycopy(elementData, r, elementData, w, size - r);

????????????????w += size - r;

????????????}

????????????//如果新数组元素个数小于之前数组的长度 ,将w之后的元素全部设置为null

????????????if (w != size) {

????????????????for (int i = w; i < size; i++)

????????????????????elementData[i] = null;

????????????????modCount += size - w;

????????????????size = w;

????????????????modified = true;

????????????}

????????}

????????return modified;

????}

?

????// 删除若干个元素

????public boolean removeAll(Collection<?> c) {

????????// 调用batchRemove函数

????????return batchRemove(c, false);

????}

????

收获:在对数组进行增删改查等操作时,应该首先检查index的合法性

删除所有不在参数集合中的元素

public boolean retainAll(Collection<?> c) {

return batchRemove(c, true);

}

?

????

三、总结

????ArrayList对于元素的操作底层实现全部是基于对数组的操作实现的,因此具有随机访问效率高,但是插入删除效率低的特点。

JDK源码分析之集合02ArrayList

标签:

原文地址:http://www.cnblogs.com/vitasyuan/p/5410435.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!