ArrayList 是 Java 集合框架中 List接口 的一个实现类。
可以说 ArrayList 是我们使用最多的 List 集合,它有以下特点:
-
容量不固定,想放多少放多少(当然有最大阈值,但一般达不到)
-
有序的(元素输出顺序与输入顺序一致)
-
元素可以为 null
-
效率高
-
size(), isEmpty(), get(), set() iterator(), ListIterator() 方法的时间复杂度都是 O(1)
-
add() 添加操作的时间复杂度平均为 O(n)
-
其他所有操作的时间复杂度几乎都是 O(n)
-
-
占用空间更小
-
对比 LinkedList,不用占用额外空间维护链表结构
-
/*
*ArrayList基础AbstractList(其实类似的绝大部分多会继承一个抽象的类,此类实现一个接口,这是多态的一种表现,模式的一种应用:个人还不成熟观点)
/
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList 的成员变量
/**
* 默认初始容量.注意:只用当使用add()方法的时候才会进行默认容器大小10;
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 用于共享的空的实例,由于是object说明其可以存储null
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 用于设定默认大小的共享的空的实例
* 与 EMPTY_ELEMENTDATA 进行区分开来,以方便何时设定大小
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 正在存放集合元素的地方
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* ArrayList的当前个数
*/
private int size;
// 集合能存放的最大的数量(莫方 20多亿了)
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
ArrayList 的关键方法
1.构造函数
ArrayList 有三种构造函数:
/**
* 指定初始化容器的大小
*/
public ArrayList(int initialCapacity) {//initialCapacity 初始化容器大小值
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* 默认是一个空的object数据
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
*将Collection类型的c转成ArrayList,并实例化
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray(); // 将c转换为数组
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;
}
}
2.添加元素:
/**ArrayList的添加方法,使用add(),如果没有设定默认容器大小,会自动设定默认的大小为10
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 用于判断容器的大小,是否针对性的进行扩容
elementData[size++] = e;
return true;
}
/*
*在指定的位置添加对应的element对象
/
public void add(int index, E element) {
rangeCheckForAdd(index); // 判断在index位置添加值是否合法
ensureCapacityInternal(size + 1); // 是否进行针对性的扩容;
System.arraycopy(elementData, index, elementData, index + 1,
size - index); //间element放入index的位置,后面的参数全部后移一位.
elementData[index] = element;
size++;
}
/**
*添加一个对应Collection类型的集合
*/
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray(); //将c全部转换为数组
int numNew = a.length; //获取c的数量
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
/*
* 在指定的index开始添加一个对应Collection类型的集合
*/
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
// 将集合从index位置总体后移移动
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
//将集合从0重新进行放置
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
3.添加元素过程中用到的方法,主要是如何对集合进行扩容:
/*
*判断index是否下标越界
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/*
*对容器进行判断,是否进行扩容
*/
private void ensureCapacityInternal(int minCapacity) {
//判断此时的集合是否是初始化状态,也就是还没有一次使用添加功能
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
/** 主要针对的是初始化时使用的是add(),还是addAll()方法,针对请进行容量的定义
*在初始化时add()肯定用DEFAULT_CAPACITY,但无法保证初始化时addAll()的实例不大于DEFAULT_CAPACITY
*DEFAULT_CAPACITY = 10
*/
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);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) //MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; MAX_ARRAY_SIZE反正非常大
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;
}
/*
* 主动给集合设定容量大小
*/
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);
}
}
4.删除
/*
* 根据序号移除
*@return 被移除的对象
*/
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; //由于用后面的覆盖,所以最后一个是没有remove前的数据,为null;
return oldValue;
}
/**
* 根据对象移除
*/
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index); // 与public E remove(int index)方法类似
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
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; // clear to let GC do its work
}
/*
* 移除Collection类型中c中的元素
*/
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c); // 判断是否为空
return batchRemove(c, false);
}
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++) //提取所有与c不相同的元素从0重新覆盖elementData
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
/*此处只有当try中有异常才会执行;
*将对比的元素,w前的和由于异常中断没有进行对比到的数据保存
*很有价值观的设定!!!!
*/
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// 将w后的元素设为null,w前面的元素是与c对象不同的元素,从新定义集合的大小为w;
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
/**
* 移除集合,也就是把所有的值转为null
*/
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
/*
*
*/
public Object clone() {
try {
// 克隆 元素本身是不会被复制的,只是引用的元素的地址
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0; // 该集合修改,添加等方法的操作次数为空
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
//返回第index个元素
E elementData(int index) {
return (E) elementData[index];
}
删除的方法比较慢,常常要移动的数据比较的,从新放置数据,并且使用循环判断删除或替代的时候要使用迭代器,用for会造成并发修改异常
5.获取,修改等;
/* 都较为非常,本质是对数组的操作
*获取指定下标的元素
*/
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
/*
*将指定的index下标,修改为element
*返回的是被修改的元素
*/
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
6.一些集合的状态判断等:
/*
* 获取集合元素的多少
*/
public int size() {
return size;
}
/*
* 判断集合是否为空
*/
public boolean isEmpty() {
return size == 0;
}
/*
* 用于判断该有元素是否存在集合中
*/
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
/*
* 从开头判断该元素是否存在,最开始出现的地方index
*/
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;
}
/*
* 从最后判断该元素是否存在,最先出现的地方的index
*/
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 Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
/*
* 将集合转入到a中,a是对象,不能是基础数据类型
*看到实现原理了,宝宝建议不要拿有值的a 怎么搞,会出事的
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size) //当a的长度小于集合是,返回本集合的数组
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size); //将集合全部转入a中从0开始
if (a.length > size)
a[size] = null;
return a;
}