标签:
1、首先看看ArrayList 的基类有哪些
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
可以总结为:
ArrayList 继承了AbstractList
实现了 List 、 RondomAcess 、 cloneable 、 serializable 对于一般的容器都会实现cloneable 用于克隆 实现serializable 实现序列化
2、在ArrayList中定义的变量
主要有4个变量,在了解这些变量之前我们需要知道一些基本信息,ArrayList 底层用的是数组实现的,初始的默认容量是10 ,
private static final int DEFAULT_CAPACITY = 10;//初始的默认容量
private static final Object[] EMPTY_ELEMENTDATA = {}; //空数组
private transient Object[] elementData;//ArrayList 中真正存数据的容器
private int size;//当前数组中的数据个数
3、ArrayList中的构造函数
ArrayList中总共有三个构造函数,分别是有初始化容量的构造函数,没有参数的构造函数,带有集合参数的构造函数
(1) 从源码中我们可以看到这只是一个很简单的 创建数组对象的过程,不过在创建对象之前需要对初始容量值判断是否<0
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
(2)无参的构造函数,这里我们需要注意的是虽然数组默认容量是10 ,但是无参的情况下数组的初始化实际是个空数组而不是创建大小为10的数组
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
(3)集合 因为是对数组操作所以 用的是Arrays.copyOf 进行复制和类型转换
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);
}
4、主要函数的分析 对于容器一般我们常用的操作是增删改查
增
(1)增加一个数的操作,size记录当前数组中的数个数所以实际操作是在数组下标为size位置赋值,然后 size++ ,但是在这之前得判断size+1是否越界,也就是是否需要进行扩容,扩容我们下面再讲,该函数的返回值是Boolean类型
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
(2)在指定的下标增加一个数的操作。在这里我们需要注意一个函数就是System.arraycopy()这个是ArrayList中常用的API,主要用于数组移位
原先的数存在elementData[]中,一共size个对象,现在我们需要在下标为index插入新对象 就需要我们将 下标[index ,size-1]范围的对象移位到[index+1,size]
操作是 System.arraycopy(elementData, index, elementData, index + 1,size - 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++;
}
(3)增加一个集合的操作。首先将集合转换为数组,再判断假如该集合数目的对象是否需要扩容,再调用System.arraycopy函数将新数组中的数复制到elementData[]中
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;
}
删
(1)删除下标为index的对象,先判断index是否有效,然后是向前移一位复制,最后size下标位置赋值为null,返回删除的对象
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);//这里可能会疑惑为什么是elementData(index)而不是elementData[index] 因为elementData(index)是ArrayList的一个内部函数,实际也是返回下标为index的对象。
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,numMoved);// 又出现了System.arraycopy。。。。当别人问我ArrayList中对什么印象最深刻那么就是这个了
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
(2)删除某个对象操作。实际上这是个遍历数组顺便比较的过程,分两个条件去遍历,当删除的对象是 null的情况可非null的情况下进行,从中我们可以看出实际上在遇到第一个符合条件的对象就返回了,所以这个操作并不删除完所有
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
(3) 删除一个集合的操作。
public boolean removeAll(Collection<?> c) {
return batchRemove(c, false);// 调用一个内部函数
}
//这个函数利用了tow poit 的思想,一个读节点,一个写节点,遍历数组当该对象不存在集合中时保留下来,最后对[w,size]范围赋值为null
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;
}
改
(1) 对index下标处的对象赋值新的对象,首先判断index是否有限,然后对数组下标为index位置赋值,返回旧对象
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
查
(1)查找下标为index 的对象 ,先判断 index是否有效,再返回下标为index的数组
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
扩容分析
上面我们在分析增加一个对象操作之前会调用 ensureCapacityInternal(size + 1) ,现在我们将沿着这条线去分析扩容的过程
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.5倍
if (newCapacity - minCapacity < 0)//还要将1.5倍旧容量与最小需要的容量比较取较大值
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);//创建新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; //从这里可以看出数组能够允许的最大扩容大小为Integer.MAX_VALUE
}
其他常用的函数
1、判空函数
public boolean isEmpty() {
return size == 0;
}
2、查当前ArrayList中的对象个数
public int size() {
return size;
}
3、是否含有某个对象
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
//同样是分为null 和非 null 去遍历
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;
}
4、将容量缩小 , 减少空间
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = Arrays.copyOf(elementData, size); //创建size大小的新数组,将旧数组的值赋值到新数组
}
}
总结: ArrayList 底层用的是数组实现,默认初始化容量为10 ,扩容规则为旧数组长度的1.5倍与当前需要的最小容量的最大值,最常用的操作是Arrays.copyOf() 和 System.arraycopy()
标签:
原文地址:http://www.cnblogs.com/sophelia-M/p/4765152.html