您的位置:首页 > 移动开发 > Android开发

ArrayList源码分析

2017-01-14 21:23 351 查看
1, 结构图

ArrayList 是android中很常见的一种数据结构,经常用,现在就来分析一下源码吧,结构图如下,



其继承结构从上往下论述。

1.1, Iterable  Iterator

这两个都是接口,实现他们的方法就可以对集合进行遍历

public interface Iterable<T> {
Iterator<T> iterator();
}
public interface Iterator<E> {
public boolean hasNext();
public E next();
public void remove();
}

2.2, Collection  AbstractCollection

Collection 接口共有15个方法,其中iterator从Iterable 继承而来。如下,



AbstractCollection是一个抽象类,继承了Collection,实现了部分方法。

2.3, List  AbstractList

List只是一个接口,提供一些方法, AbstractList实现了部分方法。list中的方法如下,




2, ArrayList分析

首先看看ArrayList的构造方法

2.1, 构造方法

ArrayList一共有三个构造方法,从最简单的开始分析,

public ArrayList() {
array = EmptyArray.OBJECT;
}
Array定义如下,

transient Object[] array;

transient关键字是啥意思?

在序列化对象时,在array前面加上transient关键字是不会进行系列化的。

那么 EmptyArray.OBJECT又是什么呢?

在EmptyArray中的定义如下,

public static final Object[] OBJECT = new Object[0];

原来构造了一个大小为0 的Object数组。

 

第二个构造方法也挺简单, 构造了一个大小为capacity 的Object数组。

public ArrayList(int capacity) {
if (capacity < 0) {
throw new IllegalArgumentException("capacity < 0: " + capacity);
}
array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
}第三个构造方法将其他集合转化为ArrayList。

2.2, add方法

Add有四个方法,

1,在array的最后添加一个元素

2,在任一位置添加一个元素

3,在array的最后添加一个集合(Collection)

4, 在任一位置添加一个集合

这4个方法的本质是一样的,以第一种为例来论述,

@Override public boolean add(E object) {
Object[] a = array;
int s = size;
if (s == a.length) {
Object[] newArray = new Object[s +
(s < (MIN_CAPACITY_INCREMENT / 2) ?
MIN_CAPACITY_INCREMENT : s >> 1)];
System.arraycopy(a, 0, newArray, 0, s);
array = a = newArray;
}
a[s] = object;
size = s + 1;
modCount++;
return true;
}
private static final int MIN_CAPACITY_INCREMENT = 12;

array最开始是大小为0的数组,是怎么添加元素的呢?

Array. Length 表示的是实际申请的数组大小,而size表示Array中已使用的数组大小。并且数组的大小翻倍申请的,太过频繁申请会影响效率。所以,使用的时候一定是size方法而不是Length方法。

 

Remove 有2种方法,

1,删除任一位置的元素

2,删除特定的元素。

 

get (intindex) 得到指定位置的元素

size()数组的大小

isEmpty() 判断array中的元素是否为空,

注意: 不要使用array. Length为0来判断。

Contains(Object object) array数组中是否包含对象

indexOf(Object object) 返回对象最开始出现的位置

lastIndexOf(Object object) 返回对象最后出现的位置

trimToSize() 将array数组简单整理

hashCode()array数组的哈希码

set(int index, E object)将第index的元素替换为object

3, 接口实现

3.1, Iterable  Iterator

继承Iterable 只是表面的,主要是继承Iterator

 @Override public Iterator<E> iterator() {
return new ArrayListIterator();
}
ArrayListIterator是什么?是ArrayList的内部类并且实现了对应的方法,这样就可以使用iterator方法来进行循环了。

private class ArrayListIterator implements Iterator<E> {
/** Number of elements remaining in this iteration */
private int remaining = size;

/** Index of element that remove() would remove, or -1 if no such elt */
private int removalIndex = -1;

/** The expected modCount value */
private int expectedModCount = modCount;

public boolean hasNext() {
return remaining != 0;
}

@SuppressWarnings("unchecked") public E next() {
ArrayList<E> ourList = ArrayList.this;
int rem = remaining;
if (ourList.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
if (rem == 0) {
throw new NoSuchElementException();
}
remaining = rem - 1;
return (E) ourList.array[removalIndex = ourList.size - rem];
}

public void remove() {
Object[] a = array;
int removalIdx = removalIndex;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
if (removalIdx < 0) {
throw new IllegalStateException();
}
System.arraycopy(a, removalIdx + 1, a, removalIdx, remaining);
a[--size] = null;  // Prevent memory leak
removalIndex = -1;
expectedModCount = ++modCount;
}
}

3.2, Cloneable

继承了Cloneable接口,就必须实现clone方法,这样该类才可以进行clone。但是,

Cloneable接口中没有任何方法?这不符合一般的逻辑啊!

Java有一个特性,就是所有的java类都继承自Object

类,而clone方法是在Object类中定义的,其定义如下,

protected Object clone() throws CloneNotSupportedException {
if (!(this instanceof Cloneable)) {
throw new CloneNotSupportedException("Class " + getClass().getName() +
" doesn't implement Cloneable");
}

return internalClone();
}

原来是这样的, Cloneable只是调用clone方法的一个声明。

1,要想调用Object类的clone方法,必须继承Cloneable接口

2,继承了Cloneable接口不一定非要实现clone方法。

问题:复制在其他地方论述。

3.3, RandomAccess

RandomAccess接口里也没有什么方法,也只是一个标记,标志着什么呢?

直接看ArrayList的equals方法,

@Override public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof List)) {
return false;
}
List<?> that = (List<?>) o;
int s = size;
if (that.size() != s) {
return false;
}
Object[] a = array;
if (that instanceof RandomAccess) {
for (int i = 0; i < s; i++) {
Object eThis = a[i];
Object ethat = that.get(i);
if (eThis == null ? ethat != null : !eThis.equals(ethat)) {
return false;
}
}
} else {  // Argument list is not random access; use its iterator
Iterator<?> it = that.iterator();
for (int i = 0; i < s; i++) {
Object eThis = a[i];
Object eThat = it.next();
if (eThis == null ? eThat != null : !eThis.equals(eThat)) {
return false;
}
}
}
return true;
}

从上面的代码中可以看出,如果一个集合继承了RandomAccess接口,那么必然会有get方法,就可以使用其get方法来获取元素,否则只能用iterator。那么可以这样理解,集合类如何继承了RandomAccess接口,那么必然会有一个get方法。

问题: get方法和 iterator 哪一个的效率更高呢?

Equals方法是判断两个集合中的每个元素是不是完全相同。

3.4 Serializable

Serializable简单说就是保存对象的实例变量状态,并且可以将保存的状态读取出来。

Serializable接口也没有任何方法,除了继承之外,还要有一个哈希码,直接看源码

private static final long serialVersionUID = 8683452581122892189L;

private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
stream.writeInt(array.length);
for (int i = 0; i < size; i++) {
stream.writeObject(array[i]);
}
}

private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
int cap = stream.readInt();
if (cap < size) {
throw new InvalidObjectException(
"Capacity: " + cap + " < size: " + size);
}
array = (cap == 0 ? EmptyArray.OBJECT : new Object[cap]);
for (int i = 0; i < size; i++) {
array[i] = stream.readObject();
}
}

由此看到,进行系列化和反系列化时,只是简单仅针对array数组值。

问题: Serializable 和 Parcelable 的区别。

4, 横向比较

从源码中看, Vector和ArrayList很像,都是继承AbstractList等,那么他们之间有哪些区别呢?

1, Vector有些方法使用关键字synchronized进行修饰,所以Vector是线程安全的,导致Vector在效率上低于ArrayList。

2,都是采用线性连续空间存储元素,当空间不足时,2个类增加方法是不同的。

ArrayList一次可以增长自身空间的一半,而Vector可以设置增长因子。

 

现在一般很少使用Vector了。

Stack继承Vector,只是多了几个方法而已,

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息