您的位置:首页 > 编程语言 > Java开发

从源码分析java集合【ArrayList】

2015-04-26 20:50 756 查看
ArrayList的内部实现其实就是我们熟悉的数组,它继承了AbstractList,实现了List,RandomAccess,Cloneable和Serializable接口。

RandomAccess接口是说明实现类是支持快速随机访问的,它的随机访问的性能非常好,通常它的List的实现类:

for (int i=0, n=list.size(); i < n; i++)
list.get(i);

//比下面这个循环更快

for (Iterator i=list.iterator(); i.hasNext(); )
i.next();
而LInkedList的sequentail access fast than random access,即顺序访问优于随机访问的。和LinkedList一样,它还实现了其他的接口。

public class ArrayList<E> extends AbstractList<E>
implements List<E>,RandomAccess,Cloneable,java.io.Serializable{
/*
* 默认的数组大小
* */
private static final int DEFAULT_CAPACITY = 10;

/*
* 用于空实例的共享空数组实例
* */
private static final Object[] EMPTY_ELEMENTDATA = {};

/*
* 用于默认大小实例的共享空数组实例,通过首次添加元素时会拓展多大来确定是调用它还是EMPRY_ELEMENTDATA
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added
* */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
*用来存储ArrayList元素的数组缓冲区,ArrayList的容量就是这个缓冲区的大小,
*任何空的ArrayList都存在elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA,
* 当第一个元素添加时,它的容量会被扩展成DEAFAULT_CAPACITY.
*/
transient Object[] elementData;

/*
* 包含元素的数量大小
* */
private int size;

...
}
与LinkedList一样,它从java.util.AbstractList继承了modCount,它们的作用是一样的,还有一点可以看出,ArrayList的默认capacity是10的。

关于EMPTY_ELEMENTDATA 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA的区别还是有点含糊,这里简单的说下两者在什么时候使用,当明确指出了capacity == 0时,就使用EMPTY_ELEMENTDATA,其余使用后者。我贴出源码可能就会很清楚了。

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);
}
}

public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

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)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}


我们依旧先来看看ArrayList的add,remove,set和get的基本操作(限于本文篇幅,没有选形参不含index的方法)

public void add(int index, E element) {
rangeCheckForAdd(index);

ensureCapacityInternal(size + 1);  // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}

public E remove(int index) {
rangeCheck(index);

modCount++;
E oldValue = elementData(index);

int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work

return oldValue;
}

public E set(int index, E element) {
rangeCheck(index);

E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}

public E get(int index) {
rangeCheck(index);

return elementData(index);
}


可以看到,这些操作在判断完index是否可用后直接利用index对elementData进行操作,相对LinkedList先要进行node(index)而言,更加简洁,效率也更高。

但是有几个方法还是要说说。

<span style="white-space:pre">	</span>//看方法名就可以猜到,判断index能否直接进行add操作
rangeCheckForAdd(index);

//也可以猜到,就是用来复制数组的,在源码中能看到,大量使用它,其实Arrays.copyOf()其实就是对它的封装
System.arraycopy();

//需要好好说的是ensureCapacityInternal();
ensureCapacityInternal();
//它其实是一个系列的方法,用来确定ArrayList的capacity的大小。我们来看看源码,能够更清楚

public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;

if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}

private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}

ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
modCount++;

// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}

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

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);
}

private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

}
//我觉得看代码已经很清楚了,总的来说是判断容量是否够用,假如够用而且大于DEFAULT_CAPACITY,
// 则不做修改;假如小于DEFAULT_CAPACITY,则使用默认容量DEFAULT_CAPACITY;假如大于DEFAULT_CAPACITY
//但是原来数组不够用,就重新开辟一个数组,大小为原来的1.5倍,并且将原来的值拷贝过去

还有一个比较好玩的方法时批量删除

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;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
//注意当contains抛出异常时,会将之后的元素不加选择的全部添加到数组中,所以,批量删除并不能保证传入的集合中的元素被全部删除

在ArrayList中,调用iterator()会返回一个内部类Itr对象。它的定义如下:

private class Itr implements Iterator<E> {
int cursor;       // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;

//方法
...
}

看它的属性和后面的解释,就能知道他的意思了。因为每次他会对原来的数组进行再封装,而且我们随机访问的时间复杂度位O(1),所以在使用ArrayList时,根本没必要调用Iterator,调用它反而会增加开销。

在ArrayList还存在一个List,SubList,它能够截取this,具体看下面源码:

private class SubList extends AbstractList<E> implements RandomAccess {
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;

SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
//方法
...
}

public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}

因为它实现了AbstractList,所以它也能完成List的方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: