ArrayList
在Android中的大红人,经常用到,但是我却没有仔细考虑过,为啥直接就new 一个ArrayList,为啥不用LinkedList,什么情况下用?所以对ArrayList分析一下,学习一下。
代码分析:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
- 继承AbstractList<E>类
AbstractList 继承自 AbstractCollection 抽象类,实现了 List 接口,实现了一些接口如 get,set,remove,且实现了迭代器Iterator,简化我们要实现一个数据结构的操作,比如实现一个顺序表Array,或实现一个链表LinkedList等。
- 实现List<E>接口
List 一个元素有序,可重复,可为null的集合,实现List接口的类,元素通过索引进行排序
- 实现RandomAccess接口
一个标记接口,支持快速随机访问
- 实现Cloneable接口
支持clone
- 实现Serizllizable接口
序列化,对象的信息可以通过网络传输,可以持久化写入存储区
属性
//默认大小
private static final int DEFAULT_CAPACITY = 10;
//数组的实现方式 顺序表实现
transient Object[] elementData;
private int size;
构造方法
//可以传入容量大小
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
//传入已有的数据集合
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
add(E e)
添加一个元素
public boolean add(E e) {
//检查容量
ensureCapacityInternal(size + 1);
// Increments modCount!!
//末尾添加
elementData[size++] = e;
return true;
}
- 首先检查数组大小容量
private void ensureCapacityInternal(int minCapacity) {
if (elementData == 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 void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //当前容量
int newCapacity = oldCapacity + (oldCapacity >> 1);//默认扩容当前容量的1/2
if (newCapacity - minCapacity < 0)
//如果扩容大小还不足以容纳,采用传进的大小进行扩容
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
//判断是否超出了最大了容量限制
// private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//将原来的数据copy到新的数组,并完成扩容操作
elementData = Arrays.copyOf(elementData, newCapacity);
}
- 添加元素默认是在末尾添加
add(int index, E element)
指定位置插入指定的元素
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
ensureCapacityInternal(size + 1); // Increments modCount!!
/**
*参数1:数据源的数组
*参数2:copy的起始位置
*参数3:新的接收数组
*参数4:新的将要赋值的起始位置
*参数5:copy的长度
/
//将index后面的数据后移一位
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将空出的index位置赋值element
elementData[index] = element;
//容量加一
size++;
}
从上面可以看出,在中间位置插入一个元素,时间复杂度为O(n)
get(int index)
public E get(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
//直接取出数组index位置的元素
return (E) elementData[index];
}
可以看出,Arraylist读取元素是比较快的O(1)
remove(int index)
public E remove(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
//记录数据结构改变的次数
modCount++;
//获取index位置的元素
E oldValue = (E) elementData[index];
//删除index的元素,其它元素移动的位数
int numMoved = size - index - 1;
//将index后面的元素前移numMoved位
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
##其它常见方法
//获取元素位置的索引
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;
}
//获取最后一个o的索引
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
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;
}
public boolean addAll(int index, Collection<? extends E> c) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
从上面的简单源码分析可以看到,ArrayList底层是以个数组实现的顺序表,在插入,修改,删除的过程中效率不高,O(n) ;在查询的时候效率较高,读取速度快O(1),在Android 手机端,我们常见的listview中对读取的速度要求较高,所以常采用Arraylist.
Other
顺序表
将表中元素一个接一个的存入一组连续的存储单元中,这种存储结构是顺序结构。
transient
java语言的关键字,变量修饰符,如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。换句话来说就是,用transient关键字标记的成员变量不参与序列化过程。